What MarTechOps is
Link to section “What MarTechOps is”In 30 seconds
Link to section “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
Link to section “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) |
| 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
Link to section “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”;[1] a US “MarTech Operations Associate” role “sits at the intersection of technical execution and campaign delivery”.[2] 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”.[3] Technology is one pillar of marketing operations, not a separate profession, so this page is classed as an overlapping synonym.
The problem it solves
Link to section “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.[4] 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”.[5] 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.[6] 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
Link to section “What MarTechOps is not”- Not campaign operations: campaigns, scoring and the hand-off SLA are MarketingOps’ work; stack decision rights are RevOps’; reporting models are AnalyticsOps’.
- Not the privacy function: lawful basis, notices and consent policy belong to PrivacyOps and counsel; MarTechOps implements the signals and proves they work.
- Not ad trafficking: ad servers and consent strings to bidders are AdOps; MarTechOps owns the tag infrastructure the pixels run in.
- Not company-wide integration engineering, which belongs to IntegrationOps.
Where it comes from and where it sits
Link to section “Where it comes from and where it sits”Origins and evolution
Link to section “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).[7]
- 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.[4]
- Consent becomes a runtime signal. The IAB Europe TCF (v2.3, adoption deadline 28 February 2026),[8] Google’s consent mode[9] and California’s opt-out signal rules[10] made consent data every tag must read.
- Browsers limit tracking. WebKit caps or deletes several kinds of script-written storage;[11] Chrome kept users’ third-party-cookie choice and in October 2025 announced that it would retire most Privacy Sandbox APIs.[12]
- 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%).[4]
Glossary and mental model
Link to section “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;[13] a separate set of draft changes to a container[14] |
| 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[8][15] |
| 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[16] |
| Identity resolution | Rules deciding which identifiers (user ID, email, device IDs) belong to one person |
| Reverse ETL | Syncing modelled warehouse data into operational tools[17] |
| 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
Link to section “How MarTechOps relates to other Ops”Diagram source (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 | MAP, CRM, consent | Architecture, sync, tags | Campaigns, scoring, consent capture |
| RevOps | Go-to-market stack | Marketing stack design | Decision rights, source of truth |
| PrivacyOps | Consent | Signals, tests, records | Policy, lawful basis, rights |
| AdOps | Pixels | Tag infrastructure | Ad servers, bidders, consent strings |
| AnalyticsOps | Event data | Collection quality | Models, dashboards |
| WebOps | Page weight | Which tags load | Core Web Vitals, releases |
| DataOps | Pipelines | Activation, identity | Pipeline engineering |
Principles and operating model
Link to section “Principles and operating model”Charter and principles
Link to section “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.
- Consent travels with the data. Defaults apply before anything runs; every tag, server container and destination honours the consent state.
- One owner per field, tag and tool, and one system of record per field.
- Change through a pipeline: workspace or sandbox, non-live test, a small group of publishers, a version to roll back to.
- Test behaviour, not configuration. Prove in a browser and in the data what fires and flows.
- Least privilege, two administrators, no shared logins.
- Remove as deliberately as you add: every tool and tag has a review date.
- Integration before features. Brinker reports “ability to integrate” as the top criterion (81.3%) of surveyed marketing ops professionals evaluating new technology.[3]
Non-goals: campaign strategy, legal policy, owning the company data platform, chasing every new tool.
Fundamentals and building blocks
Link to section “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
Link to section “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
Link to section “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.
Diagram source (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
Link to section “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 decides the source of truth for each shared object; MarTechOps decides how the marketing stack implements it.
Architecture and workflow
Link to section “Architecture and workflow”Reference architecture: the consented data path
Link to section “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.
Diagram source (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 a heavier stack).
End-to-end workflow: one consent choice from banner to tools
Link to section “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.
Diagram source (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;[18] only consented destinations receive data.
Exception paths.
- The CMP loads late. Consent mode’s
wait_for_updatemakes tags wait a set number of milliseconds for an update;[18] 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,[9] 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;[10] 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
Link to section “Practices, frameworks and tools”Core practices and playbooks
Link to section “Core practices and playbooks”Playbook: tag governance
Link to section “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”.[19] The steps show one implementation, Google Tag Manager (GTM).
- Register every tag with purpose, owner, vendor, data sent, consent purpose, contract reference and review date (Artefact 1).
- 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.[20]
- Restrict what can run. GTM reads
gtm.allowlistandgtm.blocklistfrom the data layer, for example to block Custom HTML (classcustomScripts). Two caveats: any blocklist also blocks sandboxed templates unlesssandboxedScriptsis allowlisted (Artefact 2 does this), and Google now recommends templates with template policies over these lists.[13] - Separate rights. GTM container permissions are Read, Edit (no versions or publishing), Approve (versions, no publishing) and Publish.[21] Give Publish to two or three people; require 2-step verification for Custom JavaScript variables, Custom HTML tags and user settings.[22]
- Test outside Live. Publishing a version to a QA environment does not change Live.[23] Formal approval requests exist only in Tag Manager 360; in the free edition, use the Approve permission and a written review.[24]
- Keep the rollback. Every publish records a version you can revert to.[24]
- Remove, do not block. Pause or remove unused tags; tags suppressed by trigger exceptions still ship in the container.[20]
Playbook: consent-signal plumbing
Link to section “Playbook: consent-signal plumbing”The consent policy comes from PrivacyOps; this playbook implements it.
- Map purposes to consent types and tags. Google’s types are
ad_storage,ad_user_data,ad_personalizationandanalytics_storage, plus functionality, personalisation and security storage.[9] Map non-Google tags too. - 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.[18] The region list is a PrivacyOps decision.
- Choose basic or advanced consent mode deliberately. Only advanced mode sends cookieless pings while consent is denied;[9] that is a legal call as well as a measurement one (see the judgement call).
- 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.[10] Colorado recognised GPC as its first universal opt-out mechanism,
required from 1 July 2024 for businesses within its law’s thresholds;[25] PrivacyOps keeps the
jurisdiction list. GPC arrives as
Sec-GPC: 1andnavigator.globalPrivacyControl(W3C Working Draft, 24 September 2026).[26] - 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.[27]
- Export consent records in the format PrivacyOps defines; Google’s EU user consent policy also requires retaining consent records for users in the EEA, UK and Switzerland.[28]
- Test on every CMP or container release and monthly with a scripted browser (Lab 1).
Playbook: CRM–MAP sync design
Link to section “Playbook: CRM–MAP sync design”- Write a field contract (Artefact 3): system of record, direction, blank-value rule and writers for each field.
- Know the connector. Marketo’s Salesforce sync pauses five minutes between cycles and is bidirectional only for leads, contacts and Salesforce campaigns.[29] 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.[30] Two defaults differ: Lifecycle stage “can only be moved forward” by HubSpot tools, the integration included;[31] 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.[32]
- Give every field one writer. “Prefer Salesforce unless blank” lets HubSpot write only while Salesforce is empty; after that, Salesforce always wins,[30] 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.[33][34] If marketing and sales both need a “stage”, give each a field.
- 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.[34][35] Schedule bulk updates.
- 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.
- Reconcile daily (Lab 3).
Playbook: identity resolution and the CDP
Link to section “Playbook: identity resolution and the CDP”- Rank identifiers. Segment gives immutable IDs such as
user_idthe highest priority, unique IDs such as email more than shared device IDs, and by default allows oneuser_idand five of other identifiers per profile.[36] - Block junk values. Segment warns that a hard-coded test
user_idreaching production “can cause hundreds of profiles to merge incorrectly”, and suggests blocking zeroes and dashes,-1,nullandanonymous.[36] The discipline applies to any CDP or warehouse identity model. - Alert on profiles over identifier limits and on merge spikes.
- Decide where the profile lives: the CDP Institute recognises packaged, warehouse-native (composable) and hybrid CDPs;[16] see the decision framework.
Playbook: server-side tagging and conversions APIs
Link to section “Playbook: server-side tagging and conversions APIs”- 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.[37]
- 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;[38] the guide says nothing similar about other vendors’ server tags, so this Atlas advises an explicit consent condition on each.
- Browser rules still apply. WebKit caps cookies set via third-party CNAME or IP-address cloaking at seven days.[11]
- Deduplicate. Meta’s recommended method matches the Pixel’s event ID and event name with the Conversions API’s
event_idandevent_name, within 48 hours; an alternative matchesevent_namewithfbpand/orexternal_idand generally works only for events sent first from the browser.[39] - 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).[40]
Playbook: email authentication set-up
Link to section “Playbook: email authentication set-up”MarketingOps checks campaign headers; the domain set-up is done here with the DNS owner.
- List every system that sends as your domain (MAP, CRM, support, billing, product email).
- SPF:
include,a,mx,ptr,existsandredirectcount towards a limit of 10 DNS lookups, beyond which evaluation returns “permerror”;[41] give each sending tool its own subdomain as the list grows. - 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.[42] A tool bouncing through its own domain aligns SPF only with a custom return-path on your subdomain; otherwise alignment comes from DKIM.
- DKIM: a key per tool, signing with your domain or subdomain, rotated on a schedule.
- DMARC (now RFC 9989, May 2026):[42] start at
p=nonewith aggregate reports, fix alignment, then tighten. Gmail requires SPF or DKIM from every sender and all three above 5,000 messages a day;[43] Outlook.com requires all three above 5,000 a day.[44] Full provider rules: MarketingOps.
Playbook: stack rationalisation
Link to section “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
Link to section “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[10] |
| EU ePrivacy Directive Art. 5(3); EDPB Guidelines 2/2023 | Law; regulators’ interpretation | Consent for device storage and access, pixels and tracking links included[45] |
| W3C Global Privacy Control | Draft specification | The header and JavaScript property to detect[26] |
| IAB Europe TCF v2.3; IAB Tech Lab Global Privacy Protocol (GPP) | Industry frameworks | Consent strings for advertising vendors (ad-request use in AdOps)[8][15] |
| Google EU user consent policy; consent mode | Vendor contract and API | Consent and record duties; how Google tags read consent[28][9] |
| RFC 7208 (SPF), RFC 9989 (DMARC), RFC 8058 | Internet standards | Email authentication and one-click unsubscribe[41][42][46] |
Tooling landscape
Link to section “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;[47] Matomo Tag Manager[48] | The controls in the tag governance playbook |
| Server-side collection | Receive and forward events on your domain | GTM server container[37] | Run cost, uptime, vendor support |
| Consent management | Banner, preferences, records, signals | TCF-registered CMPs (ad-product requirements in AdOps) | Region rules, GPC, symmetric choices, record export |
| CDP, packaged | Profiles, identity, activation | Segment Unify[36] among 240+ vendors listed by the CDP Institute[16] | Identity controls, residency, cost model |
| Warehouse-native activation | Model in the warehouse, sync to tools | Hightouch;[17] RudderStack (server under the source-available Elastic License 2.0)[49] | Data engineering; warehouse cost |
| Event validation | Tracking plans | Segment Protocols;[50] Snowplow schemas[51] | Block or flag violations |
| MAP and CRM | Engagement and sales records | See MarketingOps, SalesOps | Connector behaviour, API limits |
Automation and AI
Link to section “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”,[4] need registering like any other tool.
MarTechOps in practice
Link to section “MarTechOps in practice”Reference implementation: operating artefacts
Link to section “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
Link to section “Artefact 1: tag register entry”Example status: YAML syntax-checked by the Atlas validator; a record format, not a product configuration.
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
Link to section “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.
// 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.[13] A GTM consent template
uses setDefaultConsentState and updateConsentState instead.[18]
Artefact 3: CRM–MAP field contract
Link to section “Artefact 3: CRM–MAP field contract”Example status: YAML syntax-checked; the sync rule names are HubSpot’s, other connectors use different terms.
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
Link to section “Artefact 4: stack inventory record”Example status: JSON syntax-checked; figures are invented for illustration.
{
"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
Link to section “Artefact 5: email authentication records”Example status: illustrative DNS records with placeholder domains and a truncated key; not published anywhere.
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
Link to section “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
Link to section “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.
- Create a folder with a
site/subfolder and install Playwright:npm init -y && npm i playwright@1.47.2 && npx playwright install chromium. - Save the fixture as
site/index.html: an imitation CMP with opt-in for?region=euand 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).
<!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>
- Save the scenarios as
consent-cases.json(in real use,trackerscomes from the tag register and the cases from the consent policy).
Example status: executed.
{
"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 } }
]
}
- Save the test as
consent-test.mjs. For GPC cases it sendsSec-GPC: 1and setsnavigator.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.
// 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);
- Start the two local servers and run the test.
Example status: executed.
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):
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
Link to section “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.
- Save the plan as
tracking-plan.json.
Example status: executed.
{
"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]"
}
}
- Save a sample of collected events as
events.json(in practice, an export from your event pipeline or warehouse).
Example status: executed; fake data.
[
{"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"}}
]
- Save the checker as
check_events.pyand runpython3 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).
"""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):
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
Link to section “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.
- Create the two exports with
bash make_exports.sh.
Example status: executed.
#!/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"
- Save
reconcile.pyand runpython3 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.
"""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):
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.[32]
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
Link to section “Use cases and scenarios”Scenario 1 (documented): a regulator reads the cookie tool and the contracts
Link to section “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”.[27] 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
Link to section “Scenario 2 (illustrative): a new two-way mapping overwrites a sales field”Scenario 3 (illustrative): tag bloat slows the site
Link to section “Scenario 3 (illustrative): tag bloat slows the site”Measure, govern, pay
Link to section “Measure, govern, pay”Metrics and KPIs
Link to section “Metrics and KPIs”Measure behaviour, not tool or tag counts; funnel outcomes belong to MarketingOps and 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
Link to section “Privacy, security and compliance”Operational controls only; not legal advice. Rules differ by jurisdiction, and 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,[22] and keep two administrators and no shared logins (Google will not add users to a locked-out account).[21]
- 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”.[45]
- Opt-out signals are law in some places, California and Colorado among them.[25] In California, sale or sharing must stop “as soon as feasibly possible, but no later than 15 business days” after a valid request.[10]
- Contracts and integrations are part of the stack: a regulator may ask for each adtech vendor’s contract;[27] 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
Link to section “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.[6] 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);[40] warehouse compute for reverse ETL.
- API capacity is a shared budget across every CRM integration.[34]
- 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
Link to section “Assurance of the operating model”The cadences under operating model 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
Link to section “When things go wrong”Common failures and troubleshooting
Link to section “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[39] |
| 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[36] |
| 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[41] |
| Site error after a container publish | Custom HTML error or conflicting script | Console errors; diff of versions | Revert to the previous version and publish[24] |
Worked runbook: conversions halved after a consent banner release
Link to section “Worked runbook: conversions halved after a consent banner release”Diagram source (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.
- 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. - Cause. The new CMP configuration renamed a purpose, so its template no longer maps it to
ad_storageandanalytics_storage. - 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.
- 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
Link to section “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).
- “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;[20] 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.[6]
Deciding and adopting
Link to section “Deciding and adopting”When to use and when not to use
Link to section “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
Link to section “Decision framework”The most consequential choice is where the customer profile lives. The tree is this Atlas’s starting point, not a rule.
Diagram source (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”),[20] when nobody can run the server, or when the real goal is collecting data people declined to share.
Maturity model
Link to section “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
Link to section “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
Link to section “Toolkit”Checklists
Link to section “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
Link to section “Templates”Tool evaluation scorecard (weights are a suggestion; agree them before seeing vendors)
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
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
Link to section “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 |
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).
Does server-side tagging let us track people who declined cookies? No. Consent travels into the server container,[38] and the EDPB reads device access broadly, pixels included.[45] In advanced mode Google tags send cookieless pings with or without a server container;[9] whether that is allowed is a legal decision (see the judgement call).
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.
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
Link to section “Going further”Advanced topics and emerging direction
Link to section “Advanced topics and emerging direction”- Signals for known people. California applies opt-out signals to the consumer if known,[10] 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;[12] treat that as future work.
- More consent strings. IAB Tech Lab’s Global Privacy Protocol carries US state sections alongside the TCF;[15] test them.
- CDP functions moving into warehouses[4] shift the work towards data contracts and tests.
Learning path
Link to section “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; 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; AdOps |
| Portfolio | A consent-test suite, a tracking plan in CI, a documented tool sunset |
Wrap-up
Link to section “Wrap-up”Executive recap and cheat sheet
Link to section “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?
Link to section “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”.[9] The privacy team hesitates.
Option A: advanced. Google tags load before the choice with defaults denied and send cookieless pings while consent is denied.[9] 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.[53]
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.[9] GA4 behavioural modelling is lost: it needs tags loaded before the consent dialog (the advanced implementation).[53]
Assumptions. Counsel reads the EDPB guidelines, under which Article 5(3) applies to pixels and tracking links “even if this storage is not permanent”.[45] 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
Link to section “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.
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.
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.
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.
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.
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.
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
Link to section “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
Link to section “Sources and further reading”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.
MarTech Operations Specialist (PriceShape ApS) — The Hub job listing (one posting). Checked 2026-10-23. ↩︎
We’re Hiring: MarTech Operations Associate at World 50 Group — Clay community, 2026-02-07. Checked 2026-10-23. ↩︎
The state of marketing operations professionals is OK, but it’s going to get much, much better — Scott Brinker, chiefmartec, 2024-11-10. Checked 2026-10-23. ↩︎ ↩︎
2025 Marketing Technology Landscape Supergraphic: 100X growth since 2011, but now with AI — Scott Brinker (with Frans Riemersma), chiefmartec, 2025-05-06. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Marketing Technology (topic page) — Gartner, undated, on its 2025 survey (read on the mirror host gcom.pdo.aws.gartner.com). Checked 2026-10-23. ↩︎
Gartner Survey Finds 63% 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. ↩︎ ↩︎ ↩︎
Gartner confirms 70% have chief marketing technologist — Scott Brinker, chiefmartec, 2013-03-13. Checked 2026-10-23. ↩︎
Transparency and Consent Framework — IAB Europe. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
Consent mode overview — Google for Developers, updated 2026-07-30. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
California Consumer Privacy Act Regulations, effective 1 January 2026 — California Privacy Protection Agency, §§7025–7026. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Tracking Prevention in WebKit — WebKit. Checked 2026-10-23. ↩︎ ↩︎
Update on Plans for Privacy Sandbox Technologies — Google, 2025-10-17. Checked 2026-10-23. ↩︎ ↩︎
Restrict tag deployment — Google for Developers, updated 2026-07-30. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
Workspaces — Tag Manager Help, Google. Checked 2026-10-23. ↩︎
Global Privacy Protocol (GPP) — IAB Tech Lab. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
What Is a CDP? Customer Data Platform Meaning and Definition — CDP Institute, 2026 definition. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
Data activation concepts — Hightouch Docs. Checked 2026-10-23. ↩︎ ↩︎
Set up consent mode on websites — Google for Developers, updated 2026-10-13. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎
Publishing flow — Adobe Experience League, updated 2026-10-21. Checked 2026-10-23. ↩︎
Best practices for tags and tag managers — Katie Hempenius and Barry Pollard, web.dev, published 2021-07-29, updated 2022-08-24. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
Managing users and permissions — Tag Manager Help, Google. Checked 2026-10-23. ↩︎ ↩︎
Tag Manager and 2-step verification — Tag Manager Help, Google. Checked 2026-10-23. ↩︎ ↩︎
Environments — Tag Manager Help, Google. Checked 2026-10-23. ↩︎
Publishing, versions, and approvals — Tag Manager Help, Google. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
Universal Opt-Out and the Colorado Privacy Act — Colorado Attorney General. Checked 2026-10-23. ↩︎ ↩︎
Global Privacy Control (GPC) — W3C Privacy Working Group, Working Draft, 2026-09-24. Checked 2026-10-23. ↩︎ ↩︎
Stipulated Final Order, In the Matter of American Honda Motor Co., Inc. (ENF23-V-HO-2) — California Privacy Protection Agency, 2025-03-07. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
EU user consent policy — Google. Checked 2026-10-23. ↩︎ ↩︎
Understanding the Salesforce Sync — Adobe Experience League, updated 2026-10-07. Checked 2026-10-23. ↩︎
Map HubSpot properties to Salesforce fields — HubSpot Knowledge Base, updated 2026-09-16. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎
Use contact and company lifecycle stages — HubSpot Knowledge Base, updated 2026-10-20. Checked 2026-10-23. ↩︎ ↩︎
Sync Salesforce leads and contacts to HubSpot — HubSpot Knowledge Base, updated 2026-08-31. Checked 2026-10-23. ↩︎ ↩︎
Control Access to Fields — Salesforce Trailhead, Data Security module. Checked 2026-10-23. ↩︎ ↩︎
Connect HubSpot and Salesforce — HubSpot Knowledge Base (formerly titled “Install the HubSpot–Salesforce integration”), updated 2026-10-16. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
Manage your Salesforce integration settings — HubSpot Knowledge Base, updated 2026-08-12. Checked 2026-10-23. ↩︎
Identity Resolution Settings — Twilio Segment Docs. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎
An introduction to server-side tagging — Google for Developers, updated 2026-07-30. Checked 2026-10-23. ↩︎ ↩︎
Implement consent mode with server-side Tag Manager — Google for Developers, updated 2026-07-30. Checked 2026-10-23. ↩︎ ↩︎
Handling Duplicate Pixel and Conversions API Events — Meta for Developers (read via WebFetch). Checked 2026-10-23. ↩︎ ↩︎
Cloud Run setup guide — Google for Developers, updated 2026-05-12. Checked 2026-10-23. ↩︎ ↩︎
RFC 7208: Sender Policy Framework (SPF) — IETF, April 2014. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
RFC 9989: Domain-Based Message Authentication, Reporting, and Conformance (DMARC) — IETF, May 2026. Checked 2026-10-23. ↩︎ ↩︎ ↩︎
Email sender guidelines — Google Workspace Admin Help. Checked 2026-10-23. ↩︎
Policies, Practices, and Guidelines — Outlook.com Postmaster. Checked 2026-10-23. ↩︎
Guidelines 2/2023 on Technical Scope of Art. 5(3) of ePrivacy Directive, version 2.0 — European Data Protection Board, adopted 2024-10-07. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎
RFC 8058: Signaling One-Click Functionality for List Email Headers — IETF, January 2017. Checked 2026-10-23. ↩︎
Tags overview — Adobe Experience League, updated 2026-10-21. Checked 2026-10-23. ↩︎
Getting started with Tag Manager — Matomo. Checked 2026-10-23. ↩︎
rudderlabs/rudder-server — RudderStack, GitHub. Checked 2026-10-23. ↩︎
Protocols Tracking Plan — Twilio Segment Docs. Checked 2026-10-23. ↩︎
Schemas and data structures — Snowplow Docs. Checked 2026-10-23. ↩︎
Optimize Largest Contentful Paint — web.dev, updated 2025-03-31. Checked 2026-10-23. ↩︎
Behavioral modeling for consent mode — Google Analytics Help. Checked 2026-10-23. ↩︎ ↩︎