---
title: "MarTechOps"
slug: martechops
category: business
term_status: overlapping-synonym
evidence_status: verified
summary: "MarTechOps (marketing technology operations) designs and runs the plumbing of the marketing stack: how tools connect, how customer data and consent signals flow between them, which tags fire, and which tools the company keeps."
audience: [Beginners, Martech managers and marketing engineers, Marketing ops practitioners, Data and analytics engineers, Privacy and security partners, Marketing and IT leaders]
last_verified: 2026-10-23
key_takeaways:
  - "MarTechOps is not a separate discipline. It is the technology-and-data side of marketing operations, seen mostly in job titles. This Atlas gives it stack architecture and data plumbing; MarketingOps keeps campaigns and the funnel (an editorial split)."
  - "Treat consent as a signal that must reach every tag and tool: set defaults before any tag runs, honour Global Privacy Control where opt-out law applies, and test the result in a browser instead of trusting the banner settings."
  - "Every tag, integration and tool needs an owner, a purpose, a contract and a review date. California's Honda order shows the risk: adtech vendors on the site, but no contracts the company could produce."
  - "Design CRM–MAP sync field by field: one system of record per field, explicit sync rules, an API budget and a daily reconciliation, because sync tools overwrite values silently. Treat opt-outs as one-way: once set, only the person clears them."
  - "Server-side tagging moves vendor code off the page and gives control over what leaves it. It does not remove consent duties, it costs money to run, and it suits only some tags."
  - "Choose a packaged CDP, a warehouse-native (composable) approach or neither by deciding who will own identity rules and data quality; over-merged identities are a common, expensive failure."
  - "Measure the stack itself: consent-test pass rate, tracking-plan conformance, sync errors and lag, identity anomalies and licence use (Gartner reports that only 49% of martech tools were actively used in its 2025 survey)."
related: [marketingops, revops, adops, privacyops, analyticsops, webops, integrationops, dataops]
---

## What MarTechOps is

### In 30 seconds

**MarTechOps (marketing technology operations) is the work of designing and running the marketing stack as one system.**
It decides how the marketing automation platform (MAP), the CRM, the website's tags, the customer data platform or warehouse
and the ad platforms connect; which customer data and consent signals flow between them; who may change what; and which
tools the company keeps or retires.

When it works, a person who rejects advertising cookies is not tracked by an ad pixel, a web-form lead reaches the CRM with
the right owner within minutes, and nobody's profile is merged with a stranger's. When it fails, a regulator, a customer or
a renewal invoice is often the first to notice.

**Who this page is for.** Beginners curious about the "martech" half of marketing operations; martech managers, marketing
engineers and data teams; privacy and security partners; leaders deciding whether they need the role.

### At a glance

| Item | Summary |
|---|---|
| Goal | A rationalised stack in which trustworthy, consented data flows between tools |
| Typical owners | Martech manager, marketing engineer, marketing ops (technology side), with data engineering and IT |
| In scope | Stack architecture; CRM–MAP sync design; tag management; consent-signal plumbing; server-side tagging; CDPs and identity resolution; tracking plans; email authentication set-up; tool selection and retirement; admin access |
| Handed off | Campaigns and the funnel, stack decision rights, consent policy, ad delivery, reporting (see [What MarTechOps is not](#what-martechops-is-not)) |
| Key outputs | Stack inventory, tag register, field contract, tracking plan, consent test results, access reviews |
| Prerequisites | HTTP, cookies and DNS basics; how a CRM and a MAP work; reading JSON |
| Term status | **Overlapping synonym** of the technology side of marketing operations (see below) |

### Term status: a job title, not a discipline

A search on 2026-10-23 found no organisation, standard, certification or product named "MarTechOps"; the phrase appears
mostly in job titles. A Danish software company's "MarTech Operations Specialist" is to
"own and continuously improve the technology, data and automation that power our Marketing function";[^job-priceshape] a US
"MarTech Operations Associate" role "sits at the intersection of technical execution and campaign delivery".[^job-world50]
Both mix stack work with campaign work.

Summarising the MarketingOps.com survey of "more than 600 marketing ops pros", Scott Brinker wrote that "marketing ops is clearly an entangled mix of process, technology, and data with 77.8%, 72.7%, and 73.3%
of responsibilities matching those three pillars".[^brinker-mo] Technology is one pillar of marketing operations, not a
separate profession, so this page is classed as an **overlapping synonym**.

::: decision Editorial distinction used in this Atlas
**[MarketingOps](../marketingops/)** covers process, people and decisions: campaign intake and QA, lead scoring, the funnel
contract with sales, consent *capture* in forms, and measurement. **This page** covers the systems: stack architecture,
integrations and identity, tags and consent-signal *plumbing*, server-side tracking, email authentication set-up and tool
rationalisation. The split is this Atlas's choice, not an industry definition; if one team does both, read both pages.
:::

### The problem it solves

Stacks grow by accretion. The 2025 marketing technology landscape counted 15,384 products in 49 categories, up 9% in a year,
while 1,211 products from the previous edition disappeared.[^landscape-2025] Each purchase brings a tag, an integration, an
admin account and a contract. Gartner says its 2025 Marketing Technology Survey found that "Only 49% of tools are actively
used".[^gartner-topic] Its 2023 survey of 405 marketing leaders measured something else, the share of stack *capability* in
use: 33%, down from 58% in 2020, with ecosystem complexity among the main obstacles.[^gartner-2023] The measures differ, so
they are not a time series; both say much of the stack goes unused.

Without an owner for the stack as a system, the same failures recur: consent that stops at the banner; two tools that both
believe they own a field; profiles merged on a shared identifier; dead tags slowing pages; licences renewing for unused tools
and former contractors keeping admin rights.

### What MarTechOps is not

- **Not campaign operations**: campaigns, scoring and the hand-off SLA are [MarketingOps](../marketingops/)' work; stack
  decision rights are [RevOps](../revops/)'; reporting models are [AnalyticsOps](../analyticsops/)'.
- **Not the privacy function**: lawful basis, notices and consent policy belong to [PrivacyOps](../privacyops/) and counsel;
  MarTechOps implements the signals and proves they work.
- **Not ad trafficking**: ad servers and consent strings to bidders are [AdOps](../adops/); MarTechOps owns the tag
  infrastructure the pixels run in.
- **Not company-wide integration engineering**, which belongs to [IntegrationOps](../integrationops/).

## Where it comes from and where it sits

### Origins and evolution

MarTechOps has no founding document; it grew out of the technical side of marketing. Datable milestones:

- **The marketing technologist.** In 2013 Brinker reported that 70% of 203 large US firms in a Gartner survey had a "chief
  marketing technologist"-type role (the role, not the title).[^brinker-2013]
- **Landscape growth.** From 150 products in the first official landscape to 15,384 in 2025 ("100X growth since 2011"), plus
  a "hypertail" of custom-built apps and agents.[^landscape-2025]
- **Consent becomes a runtime signal.** The IAB Europe TCF (v2.3, adoption deadline 28 February 2026),[^tcf] Google's
  consent mode[^consent-mode] and California's opt-out signal rules[^ccpa-regs] made consent data every tag must read.
- **Browsers limit tracking.** WebKit caps or deletes several kinds of script-written storage;[^webkit] Chrome kept users'
  third-party-cookie choice and in October 2025 announced that it would retire most Privacy Sandbox APIs.[^sandbox]
- **The centre of the stack moves.** In Brinker and Riemersma's 2025 survey most B2B respondents named the CRM or MAP as the
  centre; among B2C and hybrid companies, CDP answers fell (26.9% to 17.4%) and cloud warehouses rose (20.9% to
  23.9%).[^landscape-2025]

### Glossary and mental model

| Term | Meaning |
|---|---|
| Tag; tag manager; container | Third-party code or pixel sending data to a vendor; the tool that loads and controls tags; one site's set of tags, triggers and variables |
| Custom HTML; template; workspace | In Google Tag Manager: a tag holding free-form code; a sandboxed tag type with declared permissions;[^gtm-restrict] a separate set of draft changes to a container[^gtm-ws] |
| Consent string (TCF, GPP) | A coded record of a person's choices passed to advertising vendors, in IAB Europe's or IAB Tech Lab's format[^tcf][^gpp] |
| Data layer | A structured object the page fills so tags read agreed values instead of scraping the page |
| Consent management platform (CMP) | The banner and preference centre that records choices and exposes them to tags |
| Consent state and default | The current choice per purpose (granted or denied), and the value used before the person chooses |
| Opt-out preference signal; GPC | A browser signal meaning "do not sell or share my data"; Global Privacy Control sends `Sec-GPC: 1` |
| Server-side tagging; conversions API | Tags run in a server container you control; vendors' server-to-server endpoints for events (Meta's is the Conversions API) |
| Customer data platform (CDP) | Software keeping a persistent, unified customer record available to other systems[^cdpi] |
| Identity resolution | Rules deciding which identifiers (user ID, email, device IDs) belong to one person |
| Reverse ETL | Syncing modelled warehouse data into operational tools[^hightouch] |
| Tracking plan | The agreed events and properties, with types and rules, that collection is checked against |
| System of record | The one tool whose value wins for a given field |
| SPF, DKIM, DMARC | DNS-based email authentication: allowed senders, signatures, and the policy tying them to the From domain |
| DPA; MQL | Data-processing agreement, the contract terms for a vendor handling personal data for you; marketing-qualified lead, one that marketing's rules judge ready for sales |

**Mental model: pipes, valves and a register.** *Pipes* (integrations, tags) move data; *valves* (consent signals, access
rights) decide what may flow; the *register* says who owns each, why it exists and when it goes. Most MarTechOps work is
keeping the three in step.

### How MarTechOps relates to other Ops

::: figure Where MarTechOps sits among its neighbours
```mermaid
flowchart TB
  RO["RevOps"] -->|"decision rights"| MT
  PO["PrivacyOps"] <-->|"policy in, records and tests out"| MT
  MO["MarketingOps"] <-->|"requirements"| MT
  WO["WebOps"] -->|"speed budgets"| MT
  IO["IntegrationOps"] -->|"standards"| MT
  MT["MarTechOps: stack and plumbing"] -->|"tag infrastructure"| AO["AdOps"]
  MT -->|"clean events"| AN["AnalyticsOps"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class MT core
  class MO,RO,PO,AO,AN,WO,IO neutral
```
Text equivalent: RevOps gives MarTechOps decision rights over the stack; MarketingOps exchanges requirements for platforms
and data flows; PrivacyOps sends consent policy and the GPC rule and gets consent records and test results back; MarTechOps supplies
tag infrastructure to AdOps and clean events to AnalyticsOps; WebOps sets tag performance budgets; IntegrationOps sets
integration standards.
:::

| Neighbour | Shared ground | MarTechOps focus | Neighbour's focus |
|---|---|---|---|
| [MarketingOps](../marketingops/) | MAP, CRM, consent | Architecture, sync, tags | Campaigns, scoring, consent capture |
| [RevOps](../revops/) | Go-to-market stack | Marketing stack design | Decision rights, source of truth |
| [PrivacyOps](../privacyops/) | Consent | Signals, tests, records | Policy, lawful basis, rights |
| [AdOps](../adops/) | Pixels | Tag infrastructure | Ad servers, bidders, consent strings |
| [AnalyticsOps](../analyticsops/) | Event data | Collection quality | Models, dashboards |
| [WebOps](../webops/) | Page weight | Which tags load | Core Web Vitals, releases |
| [DataOps](../dataops/) | Pipelines | Activation, identity | Pipeline engineering |

## Principles and operating model

### Charter and principles

This charter is **this Atlas's proposal**; no official MarTechOps manifesto exists. **Mission:** run the marketing stack so
that the right data, with a valid consent signal, reaches the right tool, and every tool earns its place.

1. **Consent travels with the data.** Defaults apply before anything runs; every tag, server container and destination
   honours the consent state.
2. **One owner per field, tag and tool**, and one system of record per field.
3. **Change through a pipeline**: workspace or sandbox, non-live test, a small group of publishers, a version to roll back to.
4. **Test behaviour, not configuration.** Prove in a browser and in the data what fires and flows.
5. **Least privilege, two administrators**, no shared logins.
6. **Remove as deliberately as you add**: every tool and tag has a review date.
7. **Integration before features.** Brinker reports "ability to integrate" as the top criterion (81.3%) of surveyed marketing
   ops professionals evaluating new technology.[^brinker-mo]

**Non-goals:** campaign strategy, legal policy, owning the company data platform, chasing every new tool.

### Fundamentals and building blocks

Five layers: **sources** (site, apps, forms, uploads); **collection** (tag manager, SDKs, server container, conversions
APIs); **consent and identity** (CMP states, identity rules); **data** (CDP or warehouse, tracking plan); **activation**
(MAP, CRM, ad tools). A **governance plane** spans them: inventory, access, change control, contracts, tests and cost.

### Roles, ownership and RACI

Small companies give all of this to one marketing ops generalist; larger ones split it across a martech manager, marketing
engineers, a data engineer and an IT or security partner. The RACI below has one Accountable (A) per row; R = responsible,
C = consulted, I = informed.

| Activity | MarTechOps | MarketingOps | PrivacyOps / legal | Data, IT and security |
|---|---|---|---|---|
| Stack architecture and integration design | A, R | C | C | C |
| Choosing or retiring a marketing tool (A: the marketing leader who holds the budget, within RevOps decision rights) | R | C | C | C (security review) |
| Tag request, build and publish | A, R | R (requests) | C (new vendors, data types) | I |
| Consent policy and purposes | R (implements) | I | A | C |
| Consent-signal tests and records | A, R | I | C | I |
| CRM–MAP field ownership and sync rules | A, R | C | I | C (CRM admin) |
| Identity rules in the CDP or warehouse | R | I | C | A (data platform owner) |
| Email authentication (SPF, DKIM, DMARC) | R | C | I | A (DNS owner) |
| Admin access reviews | R | I | I | A |

**Skills.** HTTP and browser developer tools; JavaScript sufficient to read tags; SQL; APIs and webhooks; DNS; the data
models of the MAP and CRM; enough privacy law to know when to call counsel.

### Lifecycle: a tool and a tag

**Tools** move through evaluate (integration, security and privacy), contract (data-processing and exit terms), integrate,
operate, review before renewal, and a planned sunset. **Tags** cycle faster, and many incidents can start in the
middle: a tag published untested, or kept long after its purpose ended.

::: figure Lifecycle of a marketing tag
```mermaid
flowchart TB
  A["Request: purpose, owner, data, vendor contract"] --> B{"Review: new vendor or new data type?"}
  B -->|"yes"| C["Privacy and security review"]
  B -->|"no"| D["Build in a workspace, template first"]
  C --> D
  D --> E["Test in QA environment: consent and payload"]
  E -->|"fails"| D
  E -->|"passes"| F["Publish as a new version"]
  F --> G["Monitor: errors, page speed, data arrival"]
  G --> H{"Quarterly review: still needed?"}
  H -->|"yes"| G
  H -->|"no"| I["Remove tag, triggers and variables"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class A,D,E,F,G core
  class B,H decision
  class C,I risk
```
Text equivalent: a request names purpose, owner, data and contract; a new vendor or data type triggers privacy and security
review; the tag is built in a workspace from a template, tested in a QA environment (failures return to build), published as
a version and monitored; a quarterly review keeps it or removes it with its triggers and variables.
:::

### Operating model and governance

- **Two change classes.** *Standard*: an approved vendor and template, no new data; one reviewer, weekly publish window.
  *High-risk*: a new vendor, Custom HTML, a new data type or consent logic; privacy review, QA-environment test, named
  publisher, rollback version recorded.
- **Cadences.** Weekly publish window and sync-error review; monthly Lab 1 and Lab 2 reports; quarterly tag, access and
  inventory review; a tool review before every renewal.
- **Escalation.** Tags firing without consent, mis-merged identities reaching customers and failed opt-outs are incidents;
  PrivacyOps is informed the same day.
- **Decision rights.** [RevOps](../revops/) decides the source of truth for each shared object; MarTechOps decides how the
  marketing stack implements it.

## Architecture and workflow

### Reference architecture: the consented data path

A representative design, not the only correct one. What matters is where the controls sit: consent is read at collection,
checked again at the server, stored next to the profiles and checked before each sync; one governance plane spans all.

::: figure Reference architecture of a marketing stack with consent and identity layers
```mermaid
flowchart TB
  subgraph SRC["Sources"]
    S1["Website and apps"]
    S2["Forms and offline uploads"]
  end
  subgraph COL["Collection"]
    C1["CMP: consent state"]
    C2["Tag manager and data layer"]
    C3["Server container: consent check"]
  end
  subgraph DAT["Data and identity"]
    D1["Profiles and consent records"]
    D2["Identity rules and tracking plan"]
  end
  subgraph ACT["Activation"]
    A1["MAP and CRM: consented only"]
    A2["Ad and analytics platforms"]
  end
  S1 --> C1
  C1 --> C2
  C2 --> C3
  C1 --> D1
  S2 --> D1
  C3 --> D1
  C3 --> A2
  D2 --> D1
  D1 --> A1
  A1 --> D1
  G["Governance plane: inventory, access, change control, contracts, tests, cost"]
  COL -.- G
  DAT -.- G
  ACT -.- G
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  class C1,D2 decision
  class C2,C3,D1 core
  class S1,S2,A1,A2 neutral
```
Text equivalent: the CMP sets consent first; the tag manager reads it and sends events to a server container, which
checks consent again and forwards events to ad and analytics platforms and to the warehouse or CDP, where the CMP also
sends consent records. Forms and offline uploads go straight to the warehouse or CDP, whose profiles
are governed by identity rules and the tracking plan. The warehouse or CDP syncs both ways with the MAP and CRM, sending
only profiles with the required consent. A governance plane covers collection, data and activation.
:::

**Components and their main risks.**

| Component | Responsibility | Main risk |
|---|---|---|
| CMP | Records choices per purpose and region; exposes them to tags | Wrong or late defaults; choices not exported as records |
| Tag manager | Loads tags under consent conditions; keeps versions | Anyone with Publish rights runs code on every page |
| Server container | Receives events on your domain; shapes and forwards them | A service to run; you decide what leaves |
| Warehouse or CDP | Profiles, events, identity resolution | Over-merging; personal data kept too long |
| MAP and CRM | Email, routing, sales work | Sync conflicts; opt-outs not propagated |

**Alternative.** A small business may stop at CMP, tag manager and the MAP's native CRM connector, with no CDP or server
container; often that is the right design (see [when not to use](#when-to-use-and-when-not-to-use) a heavier stack).

### End-to-end workflow: one consent choice from banner to tools

One visitor in the EEA (an opt-in region), followed through a CMP, a tag manager in **basic** consent mode and a server
container. US opt-out regions and GPC are covered in the exception paths below and in Lab 1.

::: figure How an EEA visitor's consent choice reaches tags and vendors (basic consent mode)
```mermaid
sequenceDiagram
  participant B as Browser
  participant C as CMP
  participant T as Tag manager
  participant S as Server
  participant V as Vendor
  B->>C: Page load (EEA)
  C->>T: Defaults: all denied
  Note over T,S: Basic mode: nothing sent
  B->>C: Accept analytics only
  C->>T: Analytics granted
  T->>S: Events + consent state
  S->>V: Analytics only
  Note over S,V: Ads vendor gets nothing
```
Text equivalent: an EEA visitor loads the page; the CMP sets all-denied defaults in the tag manager. In basic consent mode
Google tags send nothing before a choice (in advanced mode they would send cookieless pings). The visitor accepts analytics
only; the tag manager then sends events with the consent state, and the server forwards them to the analytics vendor only.
The ads vendor receives nothing.
:::

**Happy path.** Default set before any tag; the choice recorded with the notice version; the update applied on the same page
before navigation, as Google's guide requires;[^consent-guide] only consented destinations receive data.

**Exception paths.**

- *The CMP loads late.* Consent mode's `wait_for_update` makes tags wait a set number of milliseconds for an
  update;[^consent-guide] if the CMP keeps missing it, fix the load order rather than raising the wait.
- *A tag has no built-in consent check.* Add a consent requirement in the tag manager's consent settings,[^consent-mode] or
  replace the tag with a template.
- *The browser sends GPC where the law requires honouring it.* California treats it as an opt-out of sale or sharing,
  extended to the consumer if known;[^ccpa-regs] ads stay off even without a banner.
- *A destination cannot receive consent parameters.* Filter in the server container so it only gets data for people who
  granted the purpose.

## Practices, frameworks and tools

### Core practices and playbooks

#### Playbook: tag governance

Any tag manager needs the same controls: roles separating build from publish, a non-live environment, review, versions to
roll back to, and sandboxed templates instead of free-form code. Adobe Experience Platform Tags, for example, moves
libraries through development, staging and production, with Develop, Approve and Publish rights that "are not
inclusive".[^adobe-flow] The steps show one implementation, Google Tag Manager (GTM).

1. **Register every tag** with purpose, owner, vendor, data sent, consent purpose, contract reference and review date
   (Artefact 1).
2. **Template first.** Google's web.dev guidance ranks image pixels as the most performant tag type, then custom templates,
   then Custom HTML, and calls sandboxed templates "often safer" than full Custom HTML access.[^webdev-tags]
3. **Restrict what can run.** GTM reads `gtm.allowlist` and `gtm.blocklist` from the data layer, for example to block Custom
   HTML (class `customScripts`). Two caveats: any blocklist also blocks sandboxed templates unless `sandboxedScripts` is
   allowlisted (Artefact 2 does this), and Google now recommends templates with template policies over these
   lists.[^gtm-restrict]
4. **Separate rights.** GTM container permissions are Read, Edit (no versions or publishing), Approve (versions, no
   publishing) and Publish.[^gtm-perms] Give Publish to two or three people; require 2-step verification for Custom
   JavaScript variables, Custom HTML tags and user settings.[^gtm-2sv]
5. **Test outside Live.** Publishing a version to a QA environment does not change Live.[^gtm-env] Formal approval requests
   exist only in Tag Manager 360; in the free edition, use the Approve permission and a written review.[^gtm-versions]
6. **Keep the rollback.** Every publish records a version you can revert to.[^gtm-versions]
7. **Remove, do not block.** Pause or remove unused tags; tags suppressed by trigger exceptions still ship in the
   container.[^webdev-tags]

#### Playbook: consent-signal plumbing

The consent *policy* comes from [PrivacyOps](../privacyops/); this playbook implements it.

1. **Map purposes to consent types and tags.** Google's types are `ad_storage`, `ad_user_data`, `ad_personalization` and
   `analytics_storage`, plus functionality, personalisation and security storage.[^consent-mode] Map non-Google tags too.
2. **Set defaults before any measurement command**: a region-less default covers every visitor not matched by a regional
   one, so make it the strictest, and grant by default only in regions (ISO 3166-2) where counsel approved an opt-out
   model.[^consent-guide] The region list is a [PrivacyOps](../privacyops/) decision.
3. **Choose basic or advanced consent mode deliberately.** Only advanced mode sends cookieless pings while consent is
   denied;[^consent-mode] that is a legal call as well as a measurement one (see the
   [judgement call](#a-judgement-call-basic-or-advanced-consent-mode-in-the-eea)).
4. **Honour opt-out preference signals.** California requires online businesses to accept a signal (its example: "an HTTP
   header field or JavaScript object") plus another method, and says a cookie banner "is not by itself an acceptable
   method" for opting out of sale or sharing.[^ccpa-regs] Colorado recognised GPC as its first universal opt-out mechanism,
   required from 1 July 2024 for businesses within its law's thresholds;[^co-uoom] [PrivacyOps](../privacyops/) keeps the
   jurisdiction list. GPC arrives as `Sec-GPC: 1` and `navigator.globalPrivacyControl` (W3C Working Draft, 24 September
   2026).[^gpc]
5. **Make choices symmetric.** The CPPA found Honda's tool let people "Accept All" in one step while opting out took at least
   two clicks, and ordered a "Reject All" button.[^honda]
6. **Export consent records** in the format [PrivacyOps](../privacyops/) defines; Google's EU user consent policy also
   requires retaining consent records for users in the EEA, UK and Switzerland.[^eucp]
7. **Test on every CMP or container release and monthly** with a scripted browser (Lab 1).

#### Playbook: CRM–MAP sync design

1. **Write a field contract** (Artefact 3): system of record, direction, blank-value rule and writers for each field.
2. **Know the connector.** Marketo's Salesforce sync pauses five minutes between cycles and is bidirectional only for leads,
   contacts and Salesforce campaigns.[^marketo-sync] HubSpot's rules are "Prefer Salesforce unless blank", "Always use
   Salesforce", "Two-way" (most recent value wins; deletions propagate) and "Don't sync"; a field's first sync with no
   history takes the Salesforce value as baseline, which can overwrite a newer HubSpot value.[^hs-mapping] Two defaults
   differ: Lifecycle stage "can only be moved forward" by HubSpot tools, the integration included;[^hs-lifecycle] and
   clearing Email Opt Out in Salesforce does not resubscribe a HubSpot contact, while contacts that are not syncing (for
   example, outside the inclusion segment) get no opt-out sync at all.[^hs-sync]
3. **Give every field one writer.** "Prefer Salesforce unless blank" lets HubSpot write only while Salesforce is empty;
   after that, Salesforce always wins,[^hs-mapping] so use it for values HubSpot fills once. For a field the MAP keeps
   updating, use two-way and make the Salesforce field read-only for everyone except the connected user, whose permissions
   decide what the sync can write.[^sf-fls][^hs-install] If marketing and sales both need a "stage", give each a field.
4. **Budget API calls.** One HubSpot contact sync can take up to four API calls per record, and HubSpot asks you to allocate a
   share of the Salesforce limit to it.[^hs-install][^hs-settings] Schedule bulk updates.
5. **Make opt-outs one-way and propagate them first.** Only the person clears an opt-out, in the preference centre; never a
   sync, import or bulk job. Push it to every sending tool and check contacts outside the sync scope; a miss is an incident.
6. **Reconcile daily** (Lab 3).

#### Playbook: identity resolution and the CDP

1. **Rank identifiers.** Segment gives immutable IDs such as `user_id` the highest priority, unique IDs such as email more
   than shared device IDs, and by default allows one `user_id` and five of other identifiers per profile.[^seg-idr]
2. **Block junk values.** Segment warns that a hard-coded test `user_id` reaching production "can cause hundreds of profiles
   to merge incorrectly", and suggests blocking zeroes and dashes, `-1`, `null` and `anonymous`.[^seg-idr] The discipline
   applies to any CDP or warehouse identity model.
3. **Alert** on profiles over identifier limits and on merge spikes.
4. **Decide where the profile lives**: the CDP Institute recognises packaged, warehouse-native (composable) and hybrid
   CDPs;[^cdpi] see the [decision framework](#decision-framework).

#### Playbook: server-side tagging and conversions APIs

1. **What it does.** A server container runs "on a server that you control" and forwards events to vendors; Google says to
   use a first-party domain and production mode before going live.[^sgtm-intro]
2. **What it does not do.** Remove consent duties. In Google's design consent is set in the web container and Google product
   tags in the server are consent-aware;[^sgtm-consent] the guide says nothing similar about other vendors' server tags, so
   this Atlas advises an explicit consent condition on each.
3. **Browser rules still apply.** WebKit caps cookies set via third-party CNAME or IP-address cloaking at seven
   days.[^webkit]
4. **Deduplicate.** Meta's recommended method matches the Pixel's event ID and event name with the Conversions API's
   `event_id` and `event_name`, within 48 hours; an alternative matches `event_name` with `fbp` and/or `external_id` and
   generally works only for events sent first from the browser.[^meta-dedup]
5. **Budget it.** Google estimates about $45 a month per Cloud Run server, recommends at least two, and warns about logging
   charges above roughly a million requests a month (as of May 2026).[^sgtm-run]

#### Playbook: email authentication set-up

[MarketingOps](../marketingops/) checks campaign headers; the domain set-up is done here with the DNS owner.

1. **List every system that sends as your domain** (MAP, CRM, support, billing, product email).
2. **SPF**: `include`, `a`, `mx`, `ptr`, `exists` and `redirect` count towards a limit of 10 DNS lookups, beyond which
   evaluation returns "permerror";[^rfc7208] give each sending tool its own subdomain as the list grows.
3. **Know which domain each check sees.** SPF checks the envelope (MAIL FROM, or return-path) domain, not the visible From;
   DMARC "relies solely on SPF validation of the MAIL FROM identity" and needs it, or a DKIM signing domain, aligned with
   the From domain.[^rfc9989] A tool bouncing through its own domain aligns SPF only with a custom return-path on your
   subdomain; otherwise alignment comes from DKIM.
4. **DKIM**: a key per tool, signing with your domain or subdomain, rotated on a schedule.
5. **DMARC** (now RFC 9989, May 2026):[^rfc9989] start at `p=none` with aggregate reports, fix alignment, then tighten.
   Gmail requires SPF or DKIM from every sender and all three above 5,000 messages a day;[^gmail] Outlook.com requires all
   three above 5,000 a day.[^outlook] Full provider rules: [MarketingOps](../marketingops/#privacy-consent-and-compliance).

#### Playbook: stack rationalisation

Inventory every tool (Artefact 4); measure active users against paid seats; map tools doing the same job; decide keep,
consolidate, replace or retire, with reasons; and retire properly: export data, confirm vendor deletion, remove tags, keys,
DNS records and SSO apps, then cancel before the notice date.

### Methods, standards and frameworks

| Item | Type | What it gives MarTechOps |
|---|---|---|
| CCPA regulations §7025–7026 (California) | Law | Duty to honour opt-out preference signals; banner alone not enough[^ccpa-regs] |
| EU ePrivacy Directive Art. 5(3); EDPB Guidelines 2/2023 | Law; regulators' interpretation | Consent for device storage and access, pixels and tracking links included[^edpb] |
| W3C Global Privacy Control | Draft specification | The header and JavaScript property to detect[^gpc] |
| IAB Europe TCF v2.3; IAB Tech Lab Global Privacy Protocol (GPP) | Industry frameworks | Consent strings for advertising vendors (ad-request use in [AdOps](../adops/))[^tcf][^gpp] |
| Google EU user consent policy; consent mode | Vendor contract and API | Consent and record duties; how Google tags read consent[^eucp][^consent-mode] |
| RFC 7208 (SPF), RFC 9989 (DMARC), RFC 8058 | Internet standards | Email authentication and one-click unsubscribe[^rfc7208][^rfc9989][^rfc8058] |

### Tooling landscape

Examples checked on 2026-10-23; not endorsements.

| Category | Job | Examples | Selection points and risks |
|---|---|---|---|
| Tag management | Load and control tags | Google Tag Manager (free and 360); Adobe Experience Platform Tags;[^adobe-tags] Matomo Tag Manager[^matomo] | The controls in the tag governance playbook |
| Server-side collection | Receive and forward events on your domain | GTM server container[^sgtm-intro] | Run cost, uptime, vendor support |
| Consent management | Banner, preferences, records, signals | TCF-registered CMPs (ad-product requirements in [AdOps](../adops/)) | Region rules, GPC, symmetric choices, record export |
| CDP, packaged | Profiles, identity, activation | Segment Unify[^seg-idr] among 240+ vendors listed by the CDP Institute[^cdpi] | Identity controls, residency, cost model |
| Warehouse-native activation | Model in the warehouse, sync to tools | Hightouch;[^hightouch] RudderStack (server under the source-available Elastic License 2.0)[^rudder] | Data engineering; warehouse cost |
| Event validation | Tracking plans | Segment Protocols;[^seg-tp] Snowplow schemas[^snowplow] | Block or flag violations |
| MAP and CRM | Engagement and sales records | See [MarketingOps](../marketingops/), [SalesOps](../salesops/) | Connector behaviour, API limits |

### Automation and AI

**Automate the checks** in the cadences above (Labs 1–3 in CI or daily), plus alerts on sync errors and unregistered tags. **Use AI with limits**: language models can draft tag documentation or
explain a container diff, but should not hold publish or admin rights, where one wrong change hits every page or record.
Company-built apps and agents, the "hypertail",[^landscape-2025] need registering like any other tool.

## MarTechOps in practice

### Reference implementation: operating artefacts

One representative set-up: a B2B website with a tag manager and CMP, a MAP synced to a CRM, a warehouse and two sending
tools; names and values are placeholders.

#### Artefact 1: tag register entry

_Example status: YAML syntax-checked by the Atlas validator; a record format, not a product configuration._

```yaml
tag_id: tag-0142
name: Ads conversion - demo request
owner: martech@example.com            # a team mailbox, not a person
requested_by: demand-gen
vendor: ExampleAds Inc.
contract_ref: DPA-2026-031            # data-processing terms on file
purpose: advertising measurement
consent_purpose: ads                  # maps to ad_storage and ad_user_data
regions_blocked_without_consent: [EEA, UK, CH]
honours_gpc: true                     # stays off when Sec-GPC: 1 is sent
data_sent: [event_name, value, currency, hashed_order_id]
personal_data_in_payload: false
implementation: vendor template       # not Custom HTML
container_version_added: 87
last_consent_test: 2026-10-01
review_by: 2027-01-15
removal_plan: delete tag, trigger trg-demo-thanks, variable var-order-id
```

#### Artefact 2: consent defaults and tag restrictions in the page head

_Example status: JavaScript syntax-checked with `node --check`; not executed in a browser. `US` only illustrates the
regional mechanics (playbook step 2). What it blocks: Custom HTML tags, Custom JavaScript variables and the few built-in vendor tags in class `customScripts`; template tags keep running._

```javascript
// Runs before the tag manager snippet.
window.dataLayer = window.dataLayer || [];
function gtag() { window.dataLayer.push(arguments); }

// 1. Default for every visitor not matched below: nothing granted before a choice.
gtag('consent', 'default', {
  ad_storage: 'denied',
  ad_user_data: 'denied',
  ad_personalization: 'denied',
  analytics_storage: 'denied',
  wait_for_update: 500,
});

// 2. Only regions where counsel approved an opt-out model (illustrative):
//    granted by default, but advertising off when the browser sends GPC.
const gpc = navigator.globalPrivacyControl === true;
gtag('consent', 'default', {
  ad_storage: gpc ? 'denied' : 'granted',
  ad_user_data: gpc ? 'denied' : 'granted',
  ad_personalization: gpc ? 'denied' : 'granted',
  analytics_storage: 'granted',
  region: ['US'],
});

// 3. No Custom HTML; keep sandboxed custom templates (vendor and CMP templates) working.
//    Template policies then decide what each template may do.
window.dataLayer.push({
  'gtm.allowlist': ['sandboxedScripts'],
  'gtm.blocklist': ['customScripts'],
});
```

Allowlisting `sandboxedScripts` restricts no other tag class.[^gtm-restrict] A GTM consent template
uses `setDefaultConsentState` and `updateConsentState` instead.[^consent-guide]

#### Artefact 3: CRM–MAP field contract

_Example status: YAML syntax-checked; the sync rule names are HubSpot's, other connectors use different terms._

```yaml
contract: crm-map-fields
version: 2026-10-23
approved_by: [revops-lead, marketingops-lead, crm-admin]
fields:
  - field: marketing_stage         # custom: engaged, MQL (see Scenario 2)
    system_of_record: MAP
    rule: Two-way                   # no "always use HubSpot" rule exists
    writers: [MAP scoring only]     # Salesforce FLS read-only, except the connected user
    not_written_by: Salesforce flows, Apex or imports
    backfill: manual or bulk resync after a sample test  # no retroactive sync
  - field: sales_stage             # custom: accepted, qualified, disqualified
    system_of_record: CRM
    rule: Always use Salesforce     # HubSpot never writes; copy used for lists
    writers: [CRM users, CRM assignment rules]
  - field: phone
    system_of_record: CRM after creation  # the form fills it once, sales corrects it
    rule: Prefer Salesforce unless blank  # HubSpot writes only while Salesforce is blank
  - field: lifecycle_stage         # HubSpot default property
    rule: integration setting       # "Adjusting Lifecycle Stage"; forward only
    moving_back: clear by workflow first, e.g. disqualified leads
  - field: email_opt_out
    system_of_record: the person    # preference centre; any system may record it
    rule: default mapping           # both ways; a Salesforce clear won't resubscribe
    once_true: cleared only by the person, never by sync, import or bulk job
    scope_gap: contacts outside the sync segment get no opt-out sync; check daily
    max_lag_minutes: 15
    incident_if_breached: true
  - field: account_owner
    system_of_record: CRM
    rule: Two-way                   # HubSpot maps Owner only two-way
    writers: [CRM assignment rules] # no owner edits in the MAP
  - field: lead_source_detail
    system_of_record: MAP
    rule: Don't sync               # kept in MAP; reported from the warehouse
api_budget:
  share_of_crm_daily_limit: 0.30
  bulk_updates_window: "Sat 02:00-06:00 UTC"
```

#### Artefact 4: stack inventory record

_Example status: JSON syntax-checked; figures are invented for illustration._

```json
{
  "tool": "Example Webinar Platform",
  "category": "events",
  "business_owner": "field-marketing",
  "technical_owner": "martech",
  "contract": { "renewal": "2027-03-31", "notice_days": 60, "annual_cost": 18000 },
  "seats": { "paid": 25, "active_last_90_days": 7 },
  "integrations": ["MAP (native)", "CRM via MAP"],
  "tags_on_site": ["tag-0098"],
  "personal_data": ["name", "email", "attendance"],
  "dpa_on_file": true,
  "sso": true,
  "decision": "consolidate into MAP events module at renewal",
  "review_date": "2026-12-01"
}
```

#### Artefact 5: email authentication records

_Example status: illustrative DNS records with placeholder domains and a truncated key; not published anywhere._

```text
example.com.             TXT "v=spf1 include:_spf.mailhost.example -all"
mail.example.com.        TXT "v=spf1 include:send.mapvendor.example -all"
s2026._domainkey.mail.example.com.  TXT "v=DKIM1; k=rsa; p=MIIBIjANBg..."
_dmarc.example.com.      TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
```

A subdomain per sending tool keeps each SPF record under the 10-lookup limit, but aligns for DMARC only if the tool uses
it as its return-path; a DKIM signature from `mail.example.com` aligns with a From at `example.com` under relaxed
alignment, the default. DMARC stays at `none` until reports show every sender aligned.

### Hands-on labs

All three labs were **executed on 2026-10-23** on Linux with the versions named, against local fixtures only.

#### Lab 1 (beginner–intermediate): test which tags fire under each consent signal

**Objective.** Prove in a real browser which tag purposes fire for an EU visitor before and after a choice, and for a US
visitor with and without GPC.

**Prerequisites.** Node.js 20, Python 3, about 300 MB for a Playwright browser. Cost: none. Risk: none (local only).

**Steps.**

1. Create a folder with a `site/` subfolder and install Playwright:
   `npm init -y && npm i playwright@1.47.2 && npx playwright install chromium`.
2. Save the fixture as `site/index.html`: an imitation CMP with opt-in for `?region=eu` and opt-out for `?region=us`,
   where GPC switches the ads tag off. The "vendor" is a second local server.

_Example status: executed as the fixture for this lab (not a real CMP)._

```html
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Consent fixture</title></head>
<body>
<div id="banner">
  <button id="accept">Accept all</button>
  <button id="reject">Reject all</button>
</div>
<script>
  // Illustrative consent logic, not a real CMP. Region comes from the URL.
  const VENDOR = 'http://127.0.0.1:8002';
  const region = new URLSearchParams(location.search).get('region') || 'eu';
  const gpc = navigator.globalPrivacyControl === true;
  const fire = (path) => { new Image().src = VENDOR + path + '?t=' + Date.now(); };
  const apply = (s) => {
    if (s.analytics) fire('/analytics/collect');
    if (s.ads) fire('/ads/pixel');
  };
  if (region === 'us') {
    // Opt-out model: tags run by default; GPC opts out of sale/sharing.
    document.getElementById('banner').hidden = true;
    apply({ analytics: true, ads: !gpc });
  } else {
    // Opt-in model: nothing fires until the person chooses.
    const onClick = (id, s) => { document.getElementById(id).onclick = () => apply(s); };
    onClick('accept', { analytics: true, ads: true });
    onClick('reject', { analytics: false, ads: false });
  }
</script>
</body>
</html>
```

3. Save the scenarios as `consent-cases.json` (in real use, `trackers` comes from the tag register and the cases from the
   consent policy).

_Example status: executed._

```json
{
  "site": "http://localhost:8001/index.html",
  "settleMs": 1500,
  "trackers": [
    { "match": "127.0.0.1:8002/analytics/", "purpose": "analytics" },
    { "match": "127.0.0.1:8002/ads/", "purpose": "ads" }
  ],
  "cases": [
    { "name": "eu-no-choice", "query": "?region=eu", "gpc": false, "click": null,
      "expect": { "analytics": false, "ads": false } },
    { "name": "eu-accept", "query": "?region=eu", "gpc": false, "click": "#accept",
      "expect": { "analytics": true, "ads": true } },
    { "name": "eu-reject", "query": "?region=eu", "gpc": false, "click": "#reject",
      "expect": { "analytics": false, "ads": false } },
    { "name": "us-default", "query": "?region=us", "gpc": false, "click": null,
      "expect": { "analytics": true, "ads": true } },
    { "name": "us-gpc", "query": "?region=us", "gpc": true, "click": null,
      "expect": { "analytics": true, "ads": false } }
  ]
}
```

4. Save the test as `consent-test.mjs`. For GPC cases it sends `Sec-GPC: 1` and sets `navigator.globalPrivacyControl`; it
   records requests matching a tracker and compares the purposes fired with the expectation.

_Example status: executed with Playwright 1.47.2 (Chromium 129) and Node.js 20.19._

```javascript
// Consent-signal regression test: which tag purposes fire in each scenario?
// Usage: node consent-test.mjs consent-cases.json
import fs from 'node:fs';
import { chromium } from 'playwright';

const cfg = JSON.parse(fs.readFileSync(process.argv[2] || 'consent-cases.json', 'utf8'));
const purposeOf = (url) => cfg.trackers.find((t) => url.includes(t.match))?.purpose;
const browser = await chromium.launch();
let failed = 0;
for (const c of cfg.cases) {
  const context = await browser.newContext({
    extraHTTPHeaders: c.gpc ? { 'Sec-GPC': '1' } : {},
  });
  if (c.gpc) {
    // Browsers that send Sec-GPC also expose navigator.globalPrivacyControl.
    await context.addInitScript(() => {
      Object.defineProperty(Navigator.prototype, 'globalPrivacyControl', {
        get: () => true,
      });
    });
  }
  const seen = new Set();
  context.on('request', (r) => {
    const p = purposeOf(r.url());
    if (p) seen.add(p);
  });
  const page = await context.newPage();
  await page.goto(cfg.site + c.query, { waitUntil: 'load' });
  if (c.click) await page.click(c.click);
  await page.waitForTimeout(cfg.settleMs ?? 1500);
  const problems = Object.entries(c.expect)
    .filter(([purpose, want]) => seen.has(purpose) !== want)
    .map(([purpose, want]) => `${purpose} ${want ? 'missing' : 'fired'}`);
  if (problems.length) failed += 1;
  const fired = [...seen].sort().join(',');
  console.log(`${problems.length ? 'FAIL' : 'PASS'} ${c.name.padEnd(13)} fired=[${fired}]`
    + (problems.length ? `  ${problems.join('; ')}` : ''));
  await context.close();
}
await browser.close();
console.log(`${cfg.cases.length - failed}/${cfg.cases.length} scenarios passed`);
process.exit(failed ? 1 : 0);
```

5. Start the two local servers and run the test.

_Example status: executed._

```bash
python3 -m http.server 8001 --bind 127.0.0.1 --directory site &
python3 -m http.server 8002 --bind 127.0.0.1 --directory site &
node consent-test.mjs consent-cases.json
```

**Expected output** (from the run on 2026-10-23):

```text
PASS eu-no-choice  fired=[]
PASS eu-accept     fired=[ads,analytics]
PASS eu-reject     fired=[]
PASS us-default    fired=[ads,analytics]
PASS us-gpc        fired=[analytics]
5/5 scenarios passed
```

**Validation.** Prove the test can fail: change `ads: !gpc` to `ads: true` in the fixture and rerun. On 2026-10-23 this
printed `FAIL us-gpc ... ads fired` and `4/5 scenarios passed`, exiting with code 1, as a CI job needs. Restore the line.

**Troubleshooting.** Nothing fires: the port-8002 server is down or `match` misses the URLs (print `r.url()`). Clicks time
out: wrong selector, or no banner in that region. Flaky on real sites: wait for a specific request, not a fixed time.

**On a real site**, point `site` at staging and list vendor hosts from the register; a headless browser sees only its own
region's banner, so use the CMP's test override or run from each region.

**Teardown.** Stop both servers (`kill %1 %2` in the same shell) and delete the folder.

#### Lab 2 (intermediate): validate events against a tracking plan

**Objective.** Check events against a JSON Schema tracking plan and catch personal data in the wrong fields.

**Prerequisites.** A supported Python (3.10 or later) with `jsonschema` 4.x in a virtual environment. Cost and risk: none.

**Steps.**

1. Save the plan as `tracking-plan.json`.

_Example status: executed._

```json
{
  "version": "2026-10-23",
  "events": {
    "form_submitted": {
      "type": "object",
      "required": ["form_id", "consent_version", "page_type"],
      "properties": {
        "form_id": { "type": "string", "pattern": "^[a-z0-9-]+$" },
        "consent_version": { "type": "string", "pattern": "^cv-[0-9]{4}-[0-9]{2}$" },
        "page_type": { "enum": ["product", "pricing", "blog", "event"] },
        "campaign_id": { "type": "string" }
      },
      "additionalProperties": false
    },
    "demo_requested": {
      "type": "object",
      "required": ["form_id", "product"],
      "properties": {
        "form_id": { "type": "string" },
        "product": { "enum": ["core", "analytics"] }
      },
      "additionalProperties": false
    }
  },
  "forbidden_value_patterns": {
    "email address": "[^@\\s]+@[^@\\s]+\\.[A-Za-z]{2,}",
    "phone number": "\\+?[0-9][0-9 ()-]{7,}[0-9]"
  }
}
```

2. Save a sample of collected events as `events.json` (in practice, an export from your event pipeline or warehouse).

_Example status: executed; fake data._

```json
[
  {"event": "form_submitted",
   "properties": {"form_id": "webinar-2026-11", "consent_version": "cv-2026-09",
                  "page_type": "event", "campaign_id": "c-1042"}},
  {"event": "form_submitted",
   "properties": {"form_id": "Pricing Form", "consent_version": "cv-2026-09",
                  "page_type": "pricing"}},
  {"event": "form_submitted",
   "properties": {"form_id": "newsletter", "page_type": "blog"}},
  {"event": "demo_requested",
   "properties": {"form_id": "demo", "product": "core"}},
  {"event": "demo_requested",
   "properties": {"form_id": "demo", "product": "core", "email": "jane@example.com"}},
  {"event": "Form Submitted",
   "properties": {"form_id": "contact"}},
  {"event": "form_submitted",
   "properties": {"form_id": "contact", "consent_version": "cv-2026-09",
                  "page_type": "product", "campaign_id": "call +44 20 7946 0000"}}
]
```

3. Save the checker as `check_events.py` and run `python3 check_events.py tracking-plan.json events.json`.

_Example status: executed with jsonschema 4.23.0 and 3.2.0, identical output, on Python 3.8.10 (end of life, the only
interpreter on the test machine; not run on 3.10 or later)._

```python
"""Validate tracked events against a tracking plan (one JSON Schema per event)."""
import json
import re
import sys

from jsonschema import Draft7Validator

plan = json.load(open(sys.argv[1]))
events = json.load(open(sys.argv[2]))
validators = {name: Draft7Validator(s) for name, s in plan["events"].items()}
pii = {label: re.compile(p) for label, p in plan["forbidden_value_patterns"].items()}

clean = 0
for n, ev in enumerate(events, start=1):
    name, props = ev.get("event"), ev.get("properties", {})
    problems = []
    if name not in validators:
        problems.append(f"unknown event name {name!r}")
    else:
        for err in sorted(validators[name].iter_errors(props), key=str):
            where = "/".join(map(str, err.path)) or "(event)"
            problems.append(f"{where}: {err.message}")
    for key, value in props.items():
        for label, rx in pii.items():
            if isinstance(value, str) and rx.search(value):
                problems.append(f"{key}: possible {label} (personal data)")
    if problems:
        print(f"event {n} {name}: " + "; ".join(problems))
    else:
        clean += 1

print(f"{clean}/{len(events)} events conform ({100 * clean / len(events):.1f}%)")
sys.exit(0 if clean == len(events) else 1)
```

**Expected output** (the fifth line is wrapped here for width; the script prints it on one line):

```text
event 2 form_submitted: form_id: 'Pricing Form' does not match '^[a-z0-9-]+$'
event 3 form_submitted: (event): 'consent_version' is a required property
event 5 demo_requested: (event): Additional properties are not allowed
  ('email' was unexpected); email: possible email address (personal data)
event 6 Form Submitted: unknown event name 'Form Submitted'
event 7 form_submitted: campaign_id: possible phone number (personal data)
2/7 events conform (28.6%)
```

**Validation.** Fix the five events (lower-case `form_id`, add `consent_version`, drop `email`, rename the event, clean
`campaign_id`); the run should reach 7/7 and exit 0.

**Troubleshooting.** `ModuleNotFoundError` → install `jsonschema` in a virtual environment. Order numbers flagged as phone
numbers → tighten the pattern.

**Teardown.** Delete the files. In production, run the check in CI and on a daily sample; route personal-data hits to
PrivacyOps.

#### Lab 3 (intermediate–advanced): reconcile a CRM export with a MAP export

**Objective.** Measure coverage, field drift, consent conflicts in both directions and lag between two systems that should
agree, and fail the job when an opt-out has not reached both.

**Prerequisites.** Python 3 (standard library only) and Bash. Cost and risk: none; the data is fake.

**Steps.**

1. Create the two exports with `bash make_exports.sh`.

_Example status: executed._

```bash
#!/usr/bin/env bash
# Two small exports with deliberate drift (illustrative data, fake people).
set -euo pipefail
cat > crm.csv <<'CSV'
crm_id,email,lifecycle_stage,email_opt_out,updated_at
C001,ana@example.com,lead,0,2026-10-23T09:00:00Z
C002,ben@example.com,mql,0,2026-10-23T09:05:00Z
C003,chi@example.com,sql,0,2026-10-23T09:10:00Z
C004,dev@example.com,customer,1,2026-10-23T09:15:00Z
C005,eli@example.com,lead,0,2026-10-23T09:20:00Z
C006,fay@example.com,mql,0,2026-10-23T09:25:00Z
C007,gia@example.com,sql,1,2026-10-23T09:50:00Z
CSV
cat > map.csv <<'CSV'
crm_id,email,lifecycle_stage,unsubscribed,updated_at
C001,ana@example.com,lead,0,2026-10-23T09:01:00Z
C002,ben@example.com,lead,0,2026-10-23T08:40:00Z
C003,chi@example.com,sql,1,2026-10-23T09:30:00Z
C004,dev@example.com,customer,1,2026-10-23T09:16:00Z
C006,FAY@example.com,mql,0,2026-10-23T09:26:00Z
C007,gia@example.com,sql,0,2026-10-23T09:10:00Z
,gus@example.com,lead,0,2026-10-23T09:40:00Z
CSV
echo "wrote crm.csv and map.csv"
```

2. Save `reconcile.py` and run `python3 reconcile.py crm.csv map.csv`. The column names are fixed in the script, so a
   file with other headers is rejected instead of being turned into SQL.

_Example status: executed with Python 3.8.10 (standard library `csv` and `sqlite3`); exit code 1 because of the P1
conflicts._

```python
"""Reconcile a CRM export with a MAP export: coverage, field drift, consent conflicts."""
import csv
import sqlite3
import sys

# Fixed column names: SQL is never built from headers found in a file.
COLUMNS = {
    "crm": ["crm_id", "email", "lifecycle_stage", "email_opt_out", "updated_at"],
    "map": ["crm_id", "email", "lifecycle_stage", "unsubscribed", "updated_at"],
}
db = sqlite3.connect(":memory:")
for table, path in (("crm", sys.argv[1]), ("map", sys.argv[2])):
    with open(path, newline="") as f:
        reader = csv.DictReader(f)
        if reader.fieldnames != COLUMNS[table]:
            sys.exit(f"{path}: expected {COLUMNS[table]}, got {reader.fieldnames}")
        rows = [tuple(r[c] for c in COLUMNS[table]) for r in reader]
    db.execute(f"CREATE TABLE {table} ({', '.join(COLUMNS[table])})")
    db.executemany(f"INSERT INTO {table} VALUES (?, ?, ?, ?, ?)", rows)

q = lambda sql: db.execute(sql).fetchall()
crm_n = q("SELECT COUNT(*) FROM crm")[0][0]
matched = q("SELECT COUNT(*) FROM crm JOIN map USING (crm_id)")[0][0]
print(f"CRM records: {crm_n}; matched in MAP: {matched} ({100 * matched / crm_n:.1f}%)")
for (cid,) in q("SELECT crm_id FROM crm WHERE crm_id NOT IN "
                "(SELECT crm_id FROM map WHERE crm_id <> '')"):
    print(f"  missing in MAP: {cid}")
for (email,) in q("SELECT email FROM map WHERE crm_id = ''"):
    print(f"  MAP-only (no CRM id): {email}")

checks = {
    "email (case-insensitive)": "lower(c.email) <> lower(m.email)",
    "email (exact)": "c.email <> m.email",
    "lifecycle_stage": "c.lifecycle_stage <> m.lifecycle_stage",
}
for label, cond in checks.items():
    ids = [r[0] for r in q(f"SELECT c.crm_id FROM crm c JOIN map m USING (crm_id) "
                           f"WHERE {cond}")]
    print(f"mismatch {label}: {len(ids)}/{matched} {ids}")

# An opt-out in either system must hold in both: check both directions.
conflicts = q("SELECT crm_id, m.unsubscribed FROM crm c JOIN map m USING (crm_id) "
              "WHERE m.unsubscribed <> c.email_opt_out")
for cid, unsub in conflicts:
    side = ("MAP unsubscribed, CRM mailable" if unsub == "1"
            else "CRM opted out, MAP mailable")
    print(f"P1 consent conflict: {cid} ({side})")
lag = q("SELECT crm_id, (julianday(c.updated_at) - julianday(m.updated_at)) * 1440 "
        "FROM crm c JOIN map m USING (crm_id) WHERE c.updated_at > m.updated_at")
for cid, minutes in lag:
    if minutes > 15:
        print(f"stale in MAP by {minutes:.0f} min: {cid}")
sys.exit(1 if conflicts else 0)  # a P1 conflict fails the job, so alerts fire
```

**Expected output** (from the run on 2026-10-23):

```text
CRM records: 7; matched in MAP: 6 (85.7%)
  missing in MAP: C005
  MAP-only (no CRM id): gus@example.com
mismatch email (case-insensitive): 0/6 []
mismatch email (exact): 1/6 ['C006']
mismatch lifecycle_stage: 1/6 ['C002']
P1 consent conflict: C003 (MAP unsubscribed, CRM mailable)
P1 consent conflict: C007 (CRM opted out, MAP mailable)
stale in MAP by 25 min: C002
stale in MAP by 40 min: C007
```

**Interpretation.** C002's stage mismatch is lag (the MAP copy is 25 minutes old): fix the sync, not the data. C006 differs
only in case, which matters to tools that match emails exactly. C003 and C007 are incidents under Artefact 3; C007, opted
out in the CRM but mailable in the MAP, is the direction that sends marketing email. If C005 is outside the sync segment,
its opt-out would not sync either.[^hs-sync]

**Validation.** Set C003's `email_opt_out` to 1 in `crm.csv` and C007's `unsubscribed` to 1 in `map.csv`, then rerun: no
P1 lines, exit code 0. Rename a header and rerun: the script stops with "expected … got …".

**Troubleshooting.** Different headers → map them to the expected names first; mixed time zones → convert to UTC; large
exports → run the same SQL in the warehouse.

**Teardown.** Delete the files. Real exports hold personal data: keep them in an approved location and delete after use.

### Use cases and scenarios

#### Scenario 1 (documented): a regulator reads the cookie tool and the contracts

In a stipulated order of 7 March 2025, the California Privacy Protection Agency found that American Honda's cookie tool let
consumers "Accept All" with one click while opting out took at least two, and that Honda, which sold, shared or disclosed
website data to advertising technology companies, "could not produce contracts with these advertising technology
companies".[^honda] The order imposed a $632,500 fine and required, among other changes, a "Reject All" button, applying
Global Privacy Control to known consumers, and better contract management.

**For MarTechOps** each finding maps to a control here: symmetric choices in the CMP, GPC honoured in tag logic and linked
to known profiles, and a contract reference for every vendor in the tag register.

#### Scenario 2 (illustrative): a new two-way mapping overwrites a sales field

::: example Illustrative scenario
A company adds a custom "lead stage detail" field to HubSpot and Salesforce and maps it two-way. Records with no sync
history for the field take the Salesforce value as baseline, which can overwrite newer HubSpot values.[^hs-mapping]
Thousands of leads that marketing had moved to "MQL" revert to the old Salesforce value. **Method:** set the mapping to
"Don't sync" and restore values from a warehouse snapshot. "Prefer Salesforce unless blank" would repeat the incident: once
Salesforce holds a value it overwrites HubSpot's,[^hs-mapping] so marketing's later re-staging reverts. With RevOps, split
the field (Artefact 3): `marketing_stage`, written only by MAP scoring and read-only in Salesforce except for the connected
user,[^sf-fls] and `sales_stage` on "Always use Salesforce". Routing reads `marketing_stage = MQL`; run Lab 3 daily for two
weeks. **Success:** mismatches below 0.5% of matched records within a day. **Pitfalls:** field-level security governs
users, so the contract also bars automation from the field; test the first sync on a sample before the backfill, since
sandboxes without history hide it; and the default Lifecycle stage only moves forward,[^hs-lifecycle] so disqualified
leads keep a stale stage unless a workflow clears it.
:::

#### Scenario 3 (illustrative): tag bloat slows the site

::: example Illustrative scenario
A retailer's container holds 140 tags, 30 for campaigns that ended a year ago, kept alive by trigger exceptions;
[WebOps](../webops/) reports slow product pages, and an A/B-testing script hides the main image while it assigns an
experiment, a known cause of slow Largest Contentful Paint.[^webdev-lcp] **Method:** remove (not block) dead tags with their
triggers and variables,[^webdev-tags] move vendor Custom HTML to templates, and load the testing script only on tested
pages. **Success:** less script time and better field Core Web Vitals over four weeks, measured by WebOps. **Pitfall:**
deleting a tag whose data still feeds a finance report.
:::

## Measure, govern, pay

### Metrics and KPIs

Measure behaviour, not tool or tag counts; funnel outcomes belong to [MarketingOps](../marketingops/) and
[RevOps](../revops/).

| KPI | Definition and formula | Data source | Pitfall | Cadence |
|---|---|---|---|---|
| Consent-test pass rate | Scenarios passing ÷ scenarios run | Lab 1 in CI | Too few regions or purposes | Each release; monthly |
| Pre-consent firing | Non-essential vendor requests before a choice in opt-in regions; target 0 | Lab 1, crawls | Hard-coded tags outside the container | Monthly |
| Register coverage | Tags with complete entries ÷ tags in the container | Container export | Entries without contracts | Quarterly |
| Tracking-plan conformance | Conforming events ÷ sampled events | Lab 2, pipeline | Sampling one site only | Daily |
| Sync error rate and lag | Errored ÷ synced records; 95th-percentile lag (minutes) | Connector health, Lab 3 | Averages hide the worst records | Daily |
| Opt-out propagation | 95th-percentile minutes from an unsubscribe to suppression in every sender | Seeded test contacts; Lab 3 | Checking one direction only; missing contacts outside the sync segment | Weekly |
| Identity anomaly rate | Profiles over identifier limits or in merge spikes ÷ profiles | CDP or warehouse | Limits set too high | Weekly |
| Licence utilisation | 90-day active users ÷ paid seats; cost ÷ active users | Admin reports, SSO logs | Activity is not value | Before renewal |

**Worked calculation (illustrative month).** Register coverage: 96 ÷ 120 tags = 80%. Consent tests: 58 ÷ 60 scenarios =
96.7%. Sync errors: 312 ÷ 48,000 = 0.65%. Opt-out propagation p95: 22 minutes against a 15-minute target, a breach. The
webinar tool in Artefact 4: 7 ÷ 25 seats = 28%, or 18,000 ÷ 7 ≈ 2,571 per active user a year, the number for its renewal.

**Vanity traps.** Counts of integrations, tags or events collected say nothing about quality; modelled conversions are
estimates, not observed counts.

### Privacy, security and compliance

Operational controls only; not legal advice. Rules differ by jurisdiction, and [PrivacyOps](../privacyops/) owns the policy.

- **A tag manager deploys code.** Publish rights mean running JavaScript on every page, checkout included. Limit Publish,
  require 2-step verification for risky changes,[^gtm-2sv] and keep two administrators and no shared logins (Google will not
  add users to a locked-out account).[^gtm-perms]
- **Device access can need consent without cookies.** The EDPB's Guidelines 2/2023 (v2.0, 7 October 2024) treat tracking
  pixels and links as within Article 5(3) of the ePrivacy Directive, "even if this storage is not permanent".[^edpb]
- **Opt-out signals are law in some places**, California and Colorado among them.[^co-uoom] In California, sale or
  sharing must stop "as soon as feasibly possible, but no later than 15 business days" after a valid request.[^ccpa-regs]
- **Contracts and integrations are part of the stack**: a regulator may ask for each adtech vendor's contract;[^honda]
  integrations need dedicated users with narrow permissions, rotated keys and removal at retirement.
- **Minimise payloads**: no personal data in URLs, event names or free text (Lab 2 catches some).

### Cost, capacity and sustainability

- **Licences are usually the largest line MarTechOps can change** (the Atlas's judgement, not a survey finding). For scale,
  Gartner's 2023 CMO Spend and Strategy Survey found organisations spending 25.4% of their 2023 marketing budget on
  technology.[^gartner-2023] Utilisation reviews before renewals are the lever.
- **New layers cost money to run**: about $45 a month per Cloud Run tagging server in Google's estimate, at least two
  recommended (May 2026);[^sgtm-run] warehouse compute for reverse ETL.
- **API capacity is a shared budget** across every CRM integration.[^hs-install]
- **People time and sustainability**: a tool nobody owns costs more than its licence; fewer tags mean less script on every
  visitor's device (measure your own page weight with WebOps).

### Assurance of the operating model

The cadences under [operating model](#operating-model-and-governance) are the assurance loop. Share consent-test results
with PrivacyOps, alert on every P1 conflict from Lab 3, and treat tags missing from the register as incidents until
explained.

## When things go wrong

### Common failures and troubleshooting

| Symptom | Likely cause | Diagnosis | Fix and prevention |
|---|---|---|---|
| Conversions fall sharply after a banner change | CMP no longer sends the update; or defaults changed | GTM preview: consent state before and after a click; Lab 1 | Roll back; add the case to Lab 1 (see the runbook) |
| Vendors receive data before consent | Tag hard-coded in page HTML; default set after a tag loads | Browser network log with no interaction | Move the tag into the manager; default first in the head |
| Conversions counted twice | Pixel and Conversions API both send without matching event IDs | Compare event IDs in both payloads | Same ID and event name on both paths[^meta-dedup] |
| New leads missing in CRM | API allocation exhausted; a validation rule rejects records | Connector errors and API usage | Raise allocation or move bulk jobs; map required fields |
| Messages reach the wrong person | Profiles merged on a shared or hard-coded identifier | Profiles with many identifiers; merge spikes | Block junk values, set identifier limits, split profiles[^seg-idr] |
| Marketing email lands in junk | SPF permerror (over 10 lookups) or DMARC misalignment after adding a sender | Message headers; DMARC aggregate reports | Subdomain per sender; align DKIM[^rfc7208] |
| Site error after a container publish | Custom HTML error or conflicting script | Console errors; diff of versions | Revert to the previous version and publish[^gtm-versions] |

### Worked runbook: conversions halved after a consent banner release

::: example Illustrative scenario
Paid media reports that EEA conversions in an ad platform fell by about half overnight, after a new CMP configuration was
published the previous afternoon.
:::

::: figure Runbook for a consent-related tracking drop
```mermaid
flowchart TB
  A["Alert: conversions down after a release"] --> B["Scope: which regions, tags, platforms"]
  B --> C["Reproduce with Lab 1 and GTM preview"]
  C --> D{"Consent state updated after accept?"}
  D -->|"no"| E["Roll back CMP config or container version"]
  D -->|"yes"| F{"Tags firing before consent?"}
  F -->|"yes"| G["Incident: inform PrivacyOps today"]
  F -->|"no"| H["Real change in consent rates: report, no fix"]
  E --> I["Validate with Lab 1, watch 24 hours"]
  G --> I
  I --> J["Post-incident: add test case, require QA environment"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class A,B,C,I,J core
  class D,F decision
  class E,G,H risk
```
Text equivalent: scope the drop and reproduce it with the consent test and the tag manager's preview. If the consent state
is not updated after accepting, roll back; if tags fired before consent, it is a privacy incident (inform PrivacyOps the
same day); otherwise consent rates really changed: report, do not fix. Validate, watch 24 hours, add a test case and make
the QA environment mandatory.
:::

1. **Scope and reproduce (45 minutes).** The drop is EEA-only and hits every tag that waits for consent. Lab 1 against
   live, EEA, "Accept all": no analytics or ads requests; the tag manager's preview shows the state still `denied`.
2. **Cause.** The new CMP configuration renamed a purpose, so its template no longer maps it to `ad_storage` and
   `analytics_storage`.
3. **Act (MarTechOps lead; inform PrivacyOps and paid media).** Revert to the previous, tested configuration. Do not "fix
   forward" by granting defaults, which would fire tags without consent.
4. **Validate and communicate.** Lab 1 passes in every region. Conversions in the gap were not recorded and any modelled
   figures are estimates: annotate the reports. CMP changes become high-risk changes.

### Anti-patterns and myths

- **"Server-side tagging gets around consent and blockers."** It moves where tags run, not what you may collect (see the
  [FAQ](#faq)).
- **"A CDP will fix our data."** It unifies whatever it receives; without identity rules, a tracking plan and owners it
  unifies the mess faster.
- **"Two-way sync everything."** The last writer wins; give each field one writer, split "stage" into one field per team,
  and never let a sync clear an opt-out.
- **"Block old tags, we might need them."** Blocked tags still ship and weigh on the page;[^webdev-tags] remove them, the
  version history keeps the configuration.
- **"Give everyone Publish to move faster."** Speed comes from templates and a weekly window, not more
  publishers.
- **"One more tool will fix it."** Gartner's respondents named ecosystem complexity among the most common barriers to using
  what they had.[^gartner-2023]

## Deciding and adopting

### When to use and when not to use

**A dedicated MarTechOps role pays off when several of these hold:** different consent rules across jurisdictions; more than
one system writing customer fields; a CDP, server container or warehouse activation in use; ad spend that depends on
conversion signals; sensitive or regulated customer data; renewals that have become hard to track.

**Do not create a separate function when** one MAP and one CRM connect natively, you sell in one jurisdiction and a few tags
run on the site. Make the work a defined part of the marketing ops generalist's role, use the quarterly checklist, and
revisit as the stack grows; a team without enough work tends to add tools. **Hidden responsibilities:** a server container
is a service to run, a CDP needs identity owners, warehouse activation needs data engineers.

### Decision framework

The most consequential choice is where the customer profile lives. The tree is this Atlas's starting point, not a rule.

::: figure Deciding where customer profiles should live
```mermaid
flowchart TB
  Q1{"Do you need profiles that join several sources?"}
  Q1 -->|"no"| N["Stay with MAP and CRM: no CDP"]
  Q1 -->|"yes"| Q2{"Is there a warehouse with an owning data team?"}
  Q2 -->|"no"| P["Packaged CDP, own-store"]
  Q2 -->|"yes"| Q3{"Need real-time decisions in milliseconds?"}
  Q3 -->|"no"| W["Warehouse-native with reverse ETL"]
  Q3 -->|"yes"| H["Hybrid: warehouse models plus real-time layer"]
  P --> R["Then assign identity and consent owners"]
  W --> R
  H --> R
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class Q1,Q2,Q3 decision
  class P,W,H core
  class N,R neutral
```
Text equivalent: without a need for cross-source profiles, stay with the MAP and CRM. With it but no warehouse and owning
data team, use a packaged CDP; with them, warehouse-native activation, or a hybrid if decisions are needed in milliseconds.
Every CDP path ends by assigning identity and consent owners.
:::

**Server-side tagging: go or no-go.** Go when you must control what each vendor receives or cut page scripts, and can run
a service. No-go when key vendors lack support (it "only works with some tags"),[^webdev-tags] when
nobody can run the server, or when the real goal is collecting data people declined to share.

### Maturity model

This is **this Atlas's model**, not an industry standard.

| Level | Observable behaviour | Exit criteria to the next level |
|---|---|---|
| 1. Accidental | Tags added by whoever needs them; no inventory; shared logins | Register exists; two named admins per system |
| 2. Inventoried | Owners, purposes and contracts recorded | Field contract agreed; change classes and QA environment in use |
| 3. Governed | Changes through workspaces and review; consent defaults by region | Automated consent and tracking-plan checks |
| 4. Tested | Lab 1–3 style checks run automatically; monthly metric review | Opt-out and freshness targets met for a quarter |
| 5. Run as a product | Roadmap, service levels, cost per capability; yearly rationalisation | Sustain; revisit when law or architecture changes |

### Adoption roadmap: 30/60/90 days

| Window | Actions | Output and metric |
|---|---|---|
| Days 1–30 | Build the register from the container and tool list; end shared logins; run Lab 1 by hand | Register coverage; first consent results |
| Days 31–60 | Field contract (with RevOps); change classes; QA environment; remove dead tags; email authentication | Contract signed; DMARC reports flowing |
| Days 61–90 | Lab 1 in CI; daily reconciliation; tracking plan for top events; one utilisation review | Tests on every release; one renewal decided on evidence |

It depends on RevOps, PrivacyOps, the DNS owner, a data engineer and leaders willing to retire tools.

## Toolkit

### Checklists

**New tag preflight**

- [ ] Register entry complete: purpose, owner, vendor, data sent, consent purpose, contract reference, review date
- [ ] Vendor template used, or Custom HTML justified in writing
- [ ] Consent requirement set on the tag; behaviour checked with and without consent and with GPC
- [ ] No personal data in URLs, event names or free-text properties
- [ ] Tested in the QA environment; page errors and script time checked
- [ ] Rollback version noted; published by someone other than the builder

**CMP or container release**

- [ ] Lab 1 passes for every region and purpose; denied defaults still load before the tag manager
- [ ] Reject still as easy as accept; consent records still exported

**Quarterly review**

- [ ] Container diffed against the register; unknown tags explained or removed
- [ ] Access lists for tag manager, MAP, CRM, CDP and warehouse reviewed; leavers removed
- [ ] Sync error, lag and opt-out propagation metrics within targets, both directions
- [ ] Opt-outs checked for contacts outside the CRM–MAP sync segment
- [ ] Tools with renewals in the next two quarters have a utilisation review scheduled

**Tool sunset**

- [ ] Data exported; vendor deletion confirmed; tags, keys, integration users, SSO app and DNS records removed
- [ ] Licence cancelled before the notice date; inventory updated

### Templates

**Tool evaluation scorecard** (weights are a suggestion; agree them before seeing vendors)

```text
TOOL EVALUATION  | Tool: ____________ | Requested by: ________ | Date: ________
Criterion (weight)                           Score 1-5   Evidence
Integration with CRM/MAP/warehouse (30%)     ___         connector docs, API limits
Consent and privacy handling (20%)           ___         consent API, GPC, DPA, region
Data model fit and identity (15%)            ___         fields, objects, identifiers
Admin, access and audit (10%)                ___         SSO, roles, audit log
Overlap with existing tools (10%)            ___         inventory check
Total cost incl. people time (15%)           ___         licence, run cost, admin hours
Exit: data export and deletion terms         yes/no      contract clause
Decision: adopt / pilot / reject   Owner after adoption: ____________
```

**Stack change record**

```text
CHANGE  | ID: ______ | Class: standard / high-risk | Requested: ________
What changes (tags, mappings, integrations): ________________________________
Systems and regions affected: ______________________________________________
Data or consent impact (new vendor? new data type?): _______________________
Tests run (Lab 1 / Lab 2 / Lab 3, QA environment): ________________________
Rollback (version number or steps): ________________________________________
Reviewer: ________  Publisher: ________  Published at (UTC): ______________
```

### Deep comparisons

| Approach | Strengths | Limits | Choose when |
|---|---|---|---|
| Client-side tag manager | Simple; universal vendor support | Vendor code on visitors' devices | Few vendors, small team |
| Server-side container | Payload control; less page code | A service to run; partial vendor support | Many vendors; data-control needs |
| Packaged CDP | Fast start; built-in identity | Duplicates data; cost scales with profiles | No warehouse team |
| Warehouse-native activation | One copy of data; reuses models | Needs data engineers; real time is harder; consent state must be loaded | Mature warehouse and team |

### FAQ

**Is MarTechOps the same as marketing operations?**
Mostly: it is their technology and data pillar; the Atlas splits the pages to go deeper.

**Do we need a CDP?**
Only if you need profiles combining several sources and can name owners for identity and data quality (see the
[decision framework](#decision-framework)).

**Does server-side tagging let us track people who declined cookies?**
No. Consent travels into the server container,[^sgtm-consent] and the EDPB reads device access broadly, pixels
included.[^edpb] In advanced mode Google tags send cookieless pings with or without a server container;[^consent-mode]
whether that is allowed is a legal decision (see the [judgement call](#a-judgement-call-basic-or-advanced-consent-mode-in-the-eea)).

**Should the tag manager belong to marketing or engineering?**
To whoever can apply change control. A common split: marketing engineers build, a small group publishes, and web engineering
reviews high-risk changes and owns performance budgets with [WebOps](../webops/).

**Who owns email authentication?**
The DNS owner is accountable; MarTechOps knows which tools send as the domain and requests changes; MarketingOps checks
campaign headers.

## Going further

### Advanced topics and emerging direction

- **Signals for known people.** California applies opt-out signals to the consumer if known,[^ccpa-regs] which makes
  cross-device consent an identity problem.
- **Measurement after third-party cookies.** With the Privacy Sandbox retirement, Google said it would work on an
  interoperable attribution standard at the W3C;[^sandbox] treat that as future work.
- **More consent strings.** IAB Tech Lab's Global Privacy Protocol carries US state sections alongside the TCF;[^gpp] test them.
- **CDP functions moving into warehouses**[^landscape-2025] shift the work towards data contracts and tests.

### Learning path

| Stage | What to learn and do |
|---|---|
| Prerequisites | HTTP requests and headers, cookies, DNS; JSON; basic SQL |
| 1-day primer | This page and [MarketingOps](../marketingops/); list every third-party request in your site's network log |
| 1 week | Labs 1–3; Google's consent mode guide; a draft tag register |
| 30 days | Register and field contract for a real stack; automate Lab 1; one utilisation review |
| Specialise | Server-side tagging; identity and warehouse activation; [PrivacyOps](../privacyops/); [AdOps](../adops/) |
| Portfolio | A consent-test suite, a tracking plan in CI, a documented tool sunset |

## Wrap-up

### Executive recap and cheat sheet

| Topic | Remember |
|---|---|
| What it is | The technology and data side of marketing operations (an overlapping synonym; the split is editorial) |
| Core artefacts | Tag register, field contract, tracking plan, tool inventory, consent test suite |
| Controls | Denied defaults before tags; GPC honoured where required; few publishers; QA environment; rollback |
| Metrics | Consent-test pass rate, pre-consent firing (target 0), sync lag, opt-out propagation, licence use |
| Watch-outs | Over-merged identities, fields with two writers, opt-outs a sync can clear, tags blocked instead of removed |
| Next action | Export the container and tool list and build the register this month |

### A judgement call: basic or advanced consent mode in the EEA?

**Situation.** A retailer selling across the EEA uses Google Ads and Analytics. Paid media wants advanced consent mode, which
Google says gives "an advertiser-specific model as opposed to a general model".[^consent-mode] The privacy team hesitates.

**Option A: advanced.** Google tags load before the choice with defaults denied and send cookieless pings while consent is
denied.[^consent-mode] GA4 modelling also needs volume: at least 1,000 events a day with analytics storage denied for 7
days, and 1,000 daily users with it granted on 7 of the previous 28 days, without guaranteed eligibility.[^ga-modeling]

**Option B: basic.** Google tags wait for an interaction; with no consent nothing is sent, not even the status, and
Ads modelling uses a general model.[^consent-mode] GA4 behavioural modelling is lost: it needs tags loaded before the
consent dialog (the advanced implementation).[^ga-modeling]

**Assumptions.** Counsel reads the EDPB guidelines, under which Article 5(3) applies to pixels and tracking links "even if
this storage is not permanent".[^edpb] No regulator decision specifically on Google's cookieless pings was found for this
page. Traffic would meet the modelling thresholds.

**Why the attractive option can be wrong.** Advanced mode improves reported numbers, but whether a pre-consent ping is lawful
is a question about device access, not measurement. If counsel concludes it needs consent, a modelling gain does not offset
the risk; if not, advanced mode may be reasonable, with the reasoning recorded.

**Process.** MarTechOps captures what each mode sends before and after a choice (Lab 1 with network capture); PrivacyOps
and counsel decide per region, recording reasoning and a review date, because the uncertainty is legal; MarTechOps
implements and tests monthly.

### Learning objectives and mastery check

You should now be able to **explain** how MarTechOps differs from its neighbours; **design** a consent-signal path and a
CRM–MAP field contract; **test** consent behaviour and event quality; **choose** where profiles live; and **run** a stack
review that ends in decisions.

::: answer 1. A tag fires in the EEA before the banner is answered, but defaults show denied. Name two likely causes.
The tag is hard-coded outside the tag manager, so consent settings never apply; or the default command runs after tags
start. A non-Google tag with no consent requirement is a third possibility.
:::

::: answer 2. Why is "Two-way" a risky sync rule for a stage field, and what is different about HubSpot's lifecycle stage?
The most recent value wins and deletions propagate, so any system or bulk job writing a custom stage field overwrites the
other; "Prefer Salesforce unless blank" fails too, as Salesforce wins after the first value. Give each field one writer:
a MAP-written marketing stage, read-only in Salesforce, and a sales stage on "Always use Salesforce". HubSpot's default
Lifecycle stage only moves forward, the Salesforce integration included, so its risk is stale stages after
disqualification, handled by a workflow that clears the value.
:::

::: answer 3. A vendor says going server-side will "recover" conversions lost to consent refusals. Your answer?
Server-side changes where code runs, not what people agreed to; consent must still be honoured in the server container.
Any "recovery" comes from modelling, which is an estimate.
:::

::: answer 4. One CDP profile holds 40 email addresses. What happened and what do you change?
A shared or placeholder identifier (a hard-coded test `user_id`, "anonymous", a shared device ID) linked unrelated records.
Split the profile, block the value, set identifier limits and priorities, and alert on profiles over the limit.
:::

::: answer 5. California requires honouring GPC. Which part is MarTechOps' job?
Detecting `Sec-GPC` and `navigator.globalPrivacyControl`, mapping the signal to the purposes counsel classes as selling or
sharing, linking it to known profiles where policy says so, and testing it. The classification is PrivacyOps' and counsel's.
:::

::: answer 6. A tool has 25 paid seats, 7 active users and renews in 60 days. What do you bring?
Utilisation (28%), cost per active user, what the tool produced, overlap, exit costs, the notice date, and a
recommendation (right-size, consolidate or retire) with an owner and, if retiring, a sunset plan.
:::

### Conclusion

MarTechOps is not a new discipline. It is the part of marketing operations that
treats the stack as plumbing: pipes that move data, consent signals that act as valves, and a register of who owns each
piece. A consent choice that never reaches a tag, a sync that clears an opt-out or a profile merged on a placeholder can do
more harm than a failed campaign, and none shows on a marketing dashboard until it is expensive. Keep the register, change the stack through a pipeline, test behaviour rather than settings, and
remove tools as deliberately as you add them.

## Sources and further reading

[^job-priceshape]: [MarTech Operations Specialist (PriceShape ApS)](https://thehub.io/jobs/6abb002d838767af23aac8da) — The Hub job listing (one posting). Checked 2026-10-23.
[^job-world50]: [We're Hiring: MarTech Operations Associate at World 50 Group](https://community.clay.com/x/share-jobs/dysmbx9hrkbu/were-hiring-martech-operations-associate-at-world) — Clay community, 2026-02-07. Checked 2026-10-23.
[^brinker-mo]: [The state of marketing operations professionals is OK, but it's going to get much, much better](https://chiefmartec.com/2024/11/the-state-of-marketing-operations-professionals-is-ok-but-its-going-to-get-much-much-better/) — Scott Brinker, chiefmartec, 2024-11-10. Checked 2026-10-23.
[^brinker-2013]: [Gartner confirms 70% have chief marketing technologist](https://chiefmartec.com/2013/03/gartner-confirms-rise-of-the-chief-marketing-technologist/) — Scott Brinker, chiefmartec, 2013-03-13. Checked 2026-10-23.
[^landscape-2025]: [2025 Marketing Technology Landscape Supergraphic: 100X growth since 2011, but now with AI](https://chiefmartec.com/2025/05/2025-marketing-technology-landscape/) — Scott Brinker (with Frans Riemersma), chiefmartec, 2025-05-06. Checked 2026-10-23.
[^gartner-topic]: [Marketing Technology (topic page)](https://www.gartner.com/en/marketing/topics/marketing-technology) — Gartner, undated, on its 2025 survey (read on the mirror host gcom.pdo.aws.gartner.com). Checked 2026-10-23.
[^gartner-2023]: [Gartner Survey Finds 63% of Marketing Leaders Plan to Invest in Generative AI in the Next 24 Months](https://www.gartner.com/en/newsroom/press-releases/2023-08-23-gartner-survey-finds-63-percent-of-marketing-leaders-plan-to-invest-in-generative-ai-in-the-next-24-months) — Gartner, 2023-08-23 (read on Gartner's mirror host gcom.pdo.aws.gartner.com). Checked 2026-10-23.
[^cdpi]: [What Is a CDP? Customer Data Platform Meaning and Definition](https://www.cdpinstitute.org/learning-center/what-is-a-cdp/) — CDP Institute, 2026 definition. Checked 2026-10-23.
[^hightouch]: [Data activation concepts](https://hightouch.com/docs/getting-started/concepts) — Hightouch Docs. Checked 2026-10-23.
[^rudder]: [rudderlabs/rudder-server](https://github.com/rudderlabs/rudder-server) — RudderStack, GitHub. Checked 2026-10-23.
[^seg-idr]: [Identity Resolution Settings](https://segment.com/docs/unify/identity-resolution/identity-resolution-settings/) — Twilio Segment Docs. Checked 2026-10-23.
[^seg-tp]: [Protocols Tracking Plan](https://segment.com/docs/protocols/tracking-plan/create/) — Twilio Segment Docs. Checked 2026-10-23.
[^snowplow]: [Schemas and data structures](https://docs.snowplow.io/docs/fundamentals/schemas/) — Snowplow Docs. Checked 2026-10-23.
[^gtm-perms]: [Managing users and permissions](https://support.google.com/tagmanager/answer/6107011) — Tag Manager Help, Google. Checked 2026-10-23.
[^gtm-2sv]: [Tag Manager and 2-step verification](https://support.google.com/tagmanager/answer/4525539) — Tag Manager Help, Google. Checked 2026-10-23.
[^gtm-env]: [Environments](https://support.google.com/tagmanager/answer/6311518) — Tag Manager Help, Google. Checked 2026-10-23.
[^gtm-versions]: [Publishing, versions, and approvals](https://support.google.com/tagmanager/answer/6107163) — Tag Manager Help, Google. Checked 2026-10-23.
[^gtm-ws]: [Workspaces](https://support.google.com/tagmanager/answer/7059647) — Tag Manager Help, Google. Checked 2026-10-23.
[^gtm-restrict]: [Restrict tag deployment](https://developers.google.com/tag-platform/tag-manager/web/restrict) — Google for Developers, updated 2026-07-30. Checked 2026-10-23.
[^webdev-tags]: [Best practices for tags and tag managers](https://web.dev/articles/tag-best-practices) — Katie Hempenius and Barry Pollard, web.dev, published 2021-07-29, updated 2022-08-24. Checked 2026-10-23.
[^webdev-lcp]: [Optimize Largest Contentful Paint](https://web.dev/articles/optimize-lcp) — web.dev, updated 2025-03-31. Checked 2026-10-23.
[^adobe-tags]: [Tags overview](https://experienceleague.adobe.com/en/docs/experience-platform/tags/home) — Adobe Experience League, updated 2026-10-21. Checked 2026-10-23.
[^adobe-flow]: [Publishing flow](https://experienceleague.adobe.com/en/docs/experience-platform/tags/publish/publishing-flow) — Adobe Experience League, updated 2026-10-21. Checked 2026-10-23.
[^matomo]: [Getting started with Tag Manager](https://matomo.org/guide/tag-manager/getting-started-with-tag-manager/) — Matomo. Checked 2026-10-23.
[^sgtm-intro]: [An introduction to server-side tagging](https://developers.google.com/tag-platform/tag-manager/server-side/intro) — Google for Developers, updated 2026-07-30. Checked 2026-10-23.
[^sgtm-run]: [Cloud Run setup guide](https://developers.google.com/tag-platform/tag-manager/server-side/cloud-run-setup-guide) — Google for Developers, updated 2026-05-12. Checked 2026-10-23.
[^sgtm-consent]: [Implement consent mode with server-side Tag Manager](https://developers.google.com/tag-platform/tag-manager/server-side/consent-mode) — Google for Developers, updated 2026-07-30. Checked 2026-10-23.
[^meta-dedup]: [Handling Duplicate Pixel and Conversions API Events](https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events) — Meta for Developers (read via WebFetch). Checked 2026-10-23.
[^consent-mode]: [Consent mode overview](https://developers.google.com/tag-platform/security/concepts/consent-mode) — Google for Developers, updated 2026-07-30. Checked 2026-10-23.
[^consent-guide]: [Set up consent mode on websites](https://developers.google.com/tag-platform/security/guides/consent) — Google for Developers, updated 2026-10-13. Checked 2026-10-23.
[^ga-modeling]: [Behavioral modeling for consent mode](https://support.google.com/analytics/answer/11161109) — Google Analytics Help. Checked 2026-10-23.
[^eucp]: [EU user consent policy](https://www.google.com/about/company/user-consent-policy/) — Google. Checked 2026-10-23.
[^ccpa-regs]: [California Consumer Privacy Act Regulations, effective 1 January 2026](https://cppa.ca.gov/regulations/pdf/ccpa_statute_eff_20260101.pdf) — California Privacy Protection Agency, §§7025–7026. Checked 2026-10-23.
[^honda]: [Stipulated Final Order, In the Matter of American Honda Motor Co., Inc. (ENF23-V-HO-2)](https://cppa.ca.gov/regulations/pdf/20250307_hmc_order.pdf) — California Privacy Protection Agency, 2025-03-07. Checked 2026-10-23.
[^co-uoom]: [Universal Opt-Out and the Colorado Privacy Act](https://coag.gov/opt-out/) — Colorado Attorney General. Checked 2026-10-23.
[^gpc]: [Global Privacy Control (GPC)](https://www.w3.org/TR/gpc/) — W3C Privacy Working Group, Working Draft, 2026-09-24. Checked 2026-10-23.
[^edpb]: [Guidelines 2/2023 on Technical Scope of Art. 5(3) of ePrivacy Directive, version 2.0](https://www.edpb.europa.eu/our-work-tools/our-documents/guidelines/guidelines-22023-technical-scope-art-53-eprivacy-directive_en) — European Data Protection Board, adopted 2024-10-07. Checked 2026-10-23.
[^tcf]: [Transparency and Consent Framework](https://iabeurope.eu/transparency-consent-framework/) — IAB Europe. Checked 2026-10-23.
[^gpp]: [Global Privacy Protocol (GPP)](https://iabtechlab.com/gpp/) — IAB Tech Lab. Checked 2026-10-23.
[^webkit]: [Tracking Prevention in WebKit](https://webkit.org/tracking-prevention/) — WebKit. Checked 2026-10-23.
[^sandbox]: [Update on Plans for Privacy Sandbox Technologies](https://privacysandbox.google.com/blog/update-on-plans-for-privacy-sandbox-technologies) — Google, 2025-10-17. Checked 2026-10-23.
[^marketo-sync]: [Understanding the Salesforce Sync](https://experienceleague.adobe.com/en/docs/marketo/using/product-docs/crm-sync/salesforce-sync/understanding-the-salesforce-sync) — Adobe Experience League, updated 2026-10-07. Checked 2026-10-23.
[^hs-mapping]: [Map HubSpot properties to Salesforce fields](https://knowledge.hubspot.com/salesforce/map-hubspot-properties-to-salesforce-fields) — HubSpot Knowledge Base, updated 2026-09-16. Checked 2026-10-23.
[^hs-install]: [Connect HubSpot and Salesforce](https://knowledge.hubspot.com/salesforce/install-the-hubspot-salesforce-integration) — HubSpot Knowledge Base (formerly titled "Install the HubSpot–Salesforce integration"), updated 2026-10-16. Checked 2026-10-23.
[^hs-settings]: [Manage your Salesforce integration settings](https://knowledge.hubspot.com/salesforce/manage-your-salesforce-integration-settings) — HubSpot Knowledge Base, updated 2026-08-12. Checked 2026-10-23.
[^hs-sync]: [Sync Salesforce leads and contacts to HubSpot](https://knowledge.hubspot.com/integrations/sync-salesforce-leads-and-contacts-to-hubspot) — HubSpot Knowledge Base, updated 2026-08-31. Checked 2026-10-23.
[^hs-lifecycle]: [Use contact and company lifecycle stages](https://knowledge.hubspot.com/records/use-lifecycle-stages) — HubSpot Knowledge Base, updated 2026-10-20. Checked 2026-10-23.
[^sf-fls]: [Control Access to Fields](https://trailhead.salesforce.com/content/learn/modules/data_security/data_security_fields) — Salesforce Trailhead, Data Security module. Checked 2026-10-23.
[^rfc7208]: [RFC 7208: Sender Policy Framework (SPF)](https://www.rfc-editor.org/rfc/rfc7208.html) — IETF, April 2014. Checked 2026-10-23.
[^rfc9989]: [RFC 9989: Domain-Based Message Authentication, Reporting, and Conformance (DMARC)](https://www.rfc-editor.org/rfc/rfc9989.html) — IETF, May 2026. Checked 2026-10-23.
[^rfc8058]: [RFC 8058: Signaling One-Click Functionality for List Email Headers](https://www.rfc-editor.org/rfc/rfc8058.html) — IETF, January 2017. Checked 2026-10-23.
[^gmail]: [Email sender guidelines](https://support.google.com/a/answer/81126) — Google Workspace Admin Help. Checked 2026-10-23.
[^outlook]: [Policies, Practices, and Guidelines](https://substrate.office.com/ip-domain-management-snds/Postmaster/Policies) — Outlook.com Postmaster. Checked 2026-10-23.

**Further reading.**

- *State of Martech 2025* (Brinker and Riemersma): the full survey, linked from the landscape post.
- `research/martechops-ledger.md`: every source, what it supports, and what could not be verified.

**Changelog.**

- 2026-10-23, first version (r0): researched and written; the three labs executed on local fixtures; awaiting independent
  fact check.
- 2026-10-23, r1 revision after independent review: safe consent defaults and template allowlist; one-way opt-outs;
  HubSpot lifecycle rule; Gartner 2025 figure; Colorado GPC; Labs 2–3 re-run; diagrams, glossary, footnotes fixed.
- 2026-10-23, r2 revision after scoring: the shared stage field split into a MAP-written and a CRM-written field
  (Artefact 3, Scenario 2, sync playbook, mastery question 2); GA4 modelling needs advanced mode; repetition trimmed.
- 2026-10-23, post-score polish: Gmail and Outlook.com sender thresholds stated precisely; Gartner "most common" barriers;
  lifecycle incident claim softened; HubSpot resync wording.
