Sep 2026–present
Freelance
Client: an Adobe Experience Platform project
MarTech Solution Architect
- Architecture work on Adobe Experience Platform. Ongoing project, no public details yet.
More than eight years in consulting, five of them working with Customer Data Platforms daily. Projects delivered for my former employers’ clients are presented anonymously.
I started in development and systems integration. I then worked across analytics, tag management, and consent before focusing on Adobe Experience Platform, Real-Time CDP, AJO, and CJA.
That path helps me treat identity, activation, and measurement as parts of one architecture rather than separate configurations.
Sep 2026–present
Freelance
Client: an Adobe Experience Platform project
Feb 2025–Jul 2026
Capgemini
Client: one of Spain's top insurance companies
An insurance CDP from zero to production: the architecture, identity, consent, and the operations that keep it alive once you are gone.
What I built here
The obvious architecture does not work, and you have to measure it to find out. A journey writing to the profile leaves nothing in the data lake, so any state computed inside a journey needs nightly reconciliation to exist in SQL. Everything else follows: schemas, identity, and what persists where. With the reasons written down.
Identities resolved at ingestion with Data Prep and reconciled afterward through scheduled Query Service jobs. Duplicate profiles dropped by around 30% and unmapped emails went from 100% to under 50%.
A consent pipeline from Kafka into AEP, with a separate load for the historical state that already existed. Both cadences matter: consent arriving as streaming and consent entering through a bulk load have to end up meaning the same thing.
Queries against the profile snapshot, run weekly and logged against a baseline: totals, identity combinations, and orphans by namespace. It is not a report, it is a threshold: if profiles carrying a single identifier climb too fast, the Data Prep mapping is wrong and you see it that week.
Batch deletion of around 250,000 identities through Data Lifecycle work orders, until the licensed volume was back in range. The hard part is not deleting: it is choosing who not to delete. A candidate with an active policy or live personalization stays, and without that check the candidate list is worthless.
Requesting deletion with full scope is not enough: the reconciliation dataset was not purged with it, and the nightly chain brought the profiles back every morning. Three independent queries surfaced that, and a second bug: the snapshot stores identity keys lowercase while events send them camelCase, so the checks returned false zeros.
Personalization patterns in Adobe Journey Optimizer for the app, with the reconciliation needed to keep operational state auditable, recoverable, and transferable. Without that, a journey works until the first failure and nobody knows how to put it back.
The previous manager returned, in milliseconds, which incentive applied to each quote. Taking it apart showed a rules engine rather than a recommender, so Decisioning was not the answer for the first scope. The hard part was computing N incentives for N premiums with no loops in the expression editor.
May 2024–Feb 2025
JAKALA
Client: banking and digital education projects for a major banking institution
AEP architectures for banking and digital education: migrating channels without touching the decision engine, and deciding where each personalization value comes from.
What I built here
The official OneTrust connector ingests consent and preferences only, not cookies, and each run brings only what came after the previous one. It covers the preference center, not the consent that travels with a web event. I built both routes: the connector for identified data and event forwarding for the rest.
XDM model, identities, Web SDK, and offer decisioning for separate business lines on one instance. The first line is never the problem. The problem is keeping the second one from forcing a rewrite of the first.
I coordinated the channel migration to Adobe Journey Optimizer without touching the decision system, integrating Pega with AEP and AJO, and consolidating more than 30 email campaigns. Changing channel and decision engine at once is the fastest way to lose track of what broke.
Work on Experience Decisioning, the current decisioning capability in Adobe Journey Optimizer, rather than the Decision Management inherited from the earlier offer decisioning. The difference changes where an offer lives, how it is requested, and what the channel can decide in the moment.
Personalizing a send has two sources, and picking the wrong one is expensive. What the CDP already knows about the customer comes from the profile. What is only known at trigger time, and therefore cannot be anticipated, travels as a context attribute in the call itself. I defined which of the two feeds each value.
Retargeting offers without standing up a backend. The last ones the user viewed are kept in the browser, in a short queue fed by the card selection event. They come back with the next decisioning request, so the response does not repeat itself.
Mar 2021–Feb 2024
Accenture Song
Client: a major bank
I inherited the tagging of a major bank, built by another supplier, and ended up owning the data contract for the whole catalog and the runtime that decides what runs on the page.
What I built here
Three surfaces with three separate implementations, and therefore three versions of the truth. I rebuilt the data layer on all three against one event contract, coordinating a team of 5, with functional and technical documentation kept in Confluence.
Phone lead capture in the logged-in area ran through Walmeric. I integrated the form and its submission, and carried the Walmeric visitor identifier into the data layer, which is what ties a call back to the session behind it. On Glassbox, I improved its integration with Adobe Analytics.
You audit what you inherit, not what you built. A tag manager can rewrite the DOM and download scripts, so I ranked by risk what each piece could do across both sites. With a cookie inventory, Adobe Analytics cross-domain tracking, and the library served through a reverse proxy on its own domain.
I inherited it from another supplier and it could not be trusted, so nearly all of it was rebuilt: the data layer, the load rules, the extensions and the tags. All of it with the site in production and no downtime window, which forces you to move in pieces and prove the new one says what the old one said.
One brand, two different worlds. The commercial site and the transactional one share no tags, no consent rules and no risk profile. I kept a profile for each, with Adobe Analytics and Target in both, and most advertising tags only in the commercial one.
Campaign parameters in the URL are parsed with a per-channel grammar and mapped into Adobe Analytics variables, each with a declared meaning. It replaced an earlier position-based parser that broke as soon as an agency changed its tagging format.
Paid media pixels deployed client-side and server-side, with routing on Tealium EventStream and server-side GTM. Identifiers left the browser hashed, validated before hashing and only where consent allowed. The platform processed tens of millions of events a month.
Cross-domain consent: a decision taken on one domain had to hold on the others and survive the jump. Pre-production and production, web and app webview, categorized purposes, custom banners. And that decision reaching everything: the TCF API for vendors, Consent Mode v2 for Google tags.
The first page view happens before anyone has accepted anything. While the banner is open the page sits behind a cover, and that view is stored rather than lost or sent regardless: it is replayed afterward, following the accepted categories. Without consent nothing leaves, and with it the entry to the session survives.
A promotional component inside the authenticated area, with its placement and its click measured as their own variables rather than as one more navigation. Each position carries its own slot, which is what allows placements to be compared and then kept or pulled on evidence.
A session replay tool sees the user screen, and inside a bank logged-in area that screen is their money. Rather than trusting default masking, what it may capture is declared in a list: outside it, nothing is recorded. Installing the tool is easy. Deciding what it has a right to see is the work.
Target not as an island but as a CDP activation destination with edge personalization. Publishing audiences from analytics has a cap and takes hours to become actionable, so the rule was written the other way around: audiences are created in the CDP and never in the personalization tool.
Moving from legacy per-surface libraries to a single collection layer on the Web SDK. This is the piece that makes everything after it possible: without one collection layer, server-side, the unified profile, and the move to CJA all inherit the divergences they were meant to fix.
With the SDR maintained by hand for years, its eVars, props, and events, and the data quality control that held the reports up. The migration was not copying the old model across, but deciding which part of it still supported decisions.
Mar 2018–Mar 2021
Metriplica
Client: retail, fashion, consumer goods, travel, entertainment and automotive accounts
Standardized measurement ecosystems across several corporate accounts, and the in-house tooling to maintain them without rebuilding each time.
What I built here
Reusable GTM template containers, exported and imported into each client account: one measurement base, one for ecommerce, one per consent platform, and one for app. Standardizing retail, fashion, travel, and automotive forces you to separate the data contract from the tool that implements it.
For a fashion account, with the platform just released and none of the guidance that exists today. Both properties ran side by side in the same container through the transition, which is what lets you compare numbers and explain the differences before switching the old one off.
Native SDK implementation and resolution of the hybrid integrations between the native layer and the embedded web view. That is where sessions and identities get lost, and nobody looks until the numbers stop matching.
A Cloud Function starts a virtual machine at the scheduled time; the machine reaches the SFTP, loads the CSV files into Drive and Sheets, and shuts itself down when finished. The machine exists because of one specific constraint: the server only accepted a fixed IP, and Cloud Functions had none.
Python notebooks against the Google Analytics management API to create and update goals, filters, custom dimensions, and permissions in bulk across accounts, properties, and views. Every change was diffed against the live configuration first, and the Jira ticket was transitioned on completion.
A server-side container with custom clients, not just the stock ones. One for the legacy collection, one for the new one, one for an ad platform, plus outbound requests to an in-house API. The container was exported in March 2021, with the tool barely out.
A consumer goods brand with subsidiaries across several European markets, and one dashboard for each. Building them was not the hard part. Deciding who sees what was, so every market carried a declared recipient and access level. A report shared too widely leaks and one shared too narrowly goes unused.
Oct 2017–Mar 2018
Accenture Technology
Client: a banking client
Platforms and capabilities organized by my hands-on experience and the work I have delivered.
Bachelor's Degree in Computer Science Engineering. Universitat Autònoma de Barcelona, 2012 to 2017.
Spanish and Catalan native. English, professional working proficiency.
The full detail is on LinkedIn
Download freelance CV (PDF, 76 KB)
I separated technical proof from career history so you can review the problem first, then see where I solved it.