The Real CJA Pipeline: Seven Stages, Three Consoles, Six Handoffs
A page load becomes a Web SDK event. A Tags rule fires on it, a Datastream forks it, a Dataset stores it, a Connection combines it, a Data…
The Real CJA Pipeline: Seven Stages, Three Consoles, Six Handoffs
A page load becomes a Web SDK event. A Tags rule fires on it, a Datastream forks it, a Dataset stores it, a Connection combines it, a Data View names it, and a Workspace project finally charts it. Almost none of those six handoffs are one-to-one. The chain fans out, collapses back in, then fans out twice more, and most architecture diagrams still draw the whole thing as one arrow.

Nothing here is a single pipeline. It’s three administrative surfaces, Tags, Experience Platform, and CJA, each with its own UI, its own vocabulary, and its own person who has to be in the room when something breaks.
Personal view. The opinions and interpretations here are my own and do not represent my employer or any vendor. Mechanics are pulled from current Adobe Experience League documentation and cited at the bottom. Verify against your own tenant and entitlements before designing around them, since UI labels and defaults change.
Ask someone how a click on a website ends up as a line in a CJA report and you’ll usually get a two-box answer: “the tag fires, and it shows up in Workspace.” Both boxes are hiding an enormous amount of unstated work.
Between the click and the chart there are at minimum seven distinct constructs (a Web SDK event, a Tags rule, a Datastream, a Dataset, a Connection, a Data View, a Workspace project), and each one is configured in a different place, frequently by a different role, using a different word for roughly the same idea. A “component” in one console is a “field” in another and a “dimension” in a third. This is a map of that whole path, stage by stage, with the terminology and the gotchas that don’t survive the two-box version.
01 The shape of the pipeline
Seven stages, three admin UIs, one event
Group the seven stages by where they’re actually configured and a pattern falls out that the product names alone don’t tell you: this is three separately owned administrative surfaces. Tags (formerly Launch) owns collection. Experience Platform owns routing and storage. CJA owns modeling and reporting. An event crosses all three before it becomes a chart, and nothing forces those three surfaces to agree with each other. They’re reconciled by convention and configuration, day to day, rather than by anything the platform enforces automatically. Their underlying administration isn’t fully separate either. Tags, AEP, and CJA all sit under the same Adobe Experience Cloud/AEP account administration. Operationally, though, each one is still configured and owned on its own.
The mental model
Tags / Data Collection UI turns a browser action into one outbound request, using a rules engine and a Web SDK instance a tag developer configures. Experience Platform UI receives that request at the Edge Network, routes it per a Datastream configuration, and lands it in a schema-bound Dataset. CJA UI combines those datasets in a Connection, optionally reconciles identities through stitching, gives the resulting fields analyst-friendly names in a Data View, and exposes them to Workspace projects.
The chain isn’t a sequence of one-to-one relationships. The first two hops are indirect. The middle two, Datastream↔Dataset and Dataset↔Connection, are genuinely many-to-many: a lot messier than the tidy fan-in a single-site implementation makes them look like. The final two stay a clean one-to-many. A datastream can route to more than one dataset, and the same dataset can sit inside more than one connection. Once you’re running more than one website, expect that many-to-many middle by default:
End-to-end logical diagram: what backs how many of what

Cardinality here is configuration-time (how many of the next construct one instance of this one can reference), not per-request volume. The first two hops are marked “loose” on purpose: a rule invokes a Web SDK instance, and that instance’s environment config points at a datastream. Real flexibility, but not a formal peer relationship like the rest of this chain. Datastream↔Dataset and Dataset↔Connection are the two genuinely many-to-many hops: a datastream can route different event types to more than one dataset, and the same dataset can be included in more than one connection. Connection→Data View and Data View→Project stay a clean one-to-many. Fig 3 — A real multi-website scenario, worked through end to end
A real multi-website scenario, worked through end to end

Solid lines are the default route, where most events land. Dashed lines are exception routing rules (write a rule only for the events that should deviate, per Adobe’s dynamic-datastream guidance): here, orders and profile updates are redirected into a shared Global dataset instead of the local one. A matched rule replaces the destination; it doesn’t duplicate the event into both. The amber lines are the reason this figure exists: the Global dataset isn’t owned by one connection, it’s referenced by two. Both connections sit in the same sandbox, since a dataset can’t live in two sandboxes at once, but each one is still its own admin object with its own owner and its own stitching method. The right half shows the clean part of the chain. Full-Funnel backs two differently-scoped Data Views (marketing wants attribution detail, execs want a rollup), Regional Ops backs its own, and every Data View fans out to however many Workspace projects analysts actually build on it.
02 Collection
One request, configured in a rules engine
Adobe Experience Platform Tags (the current name for what most people still call Launch) is a rules engine that ships JavaScript to a browser. Three components do the work:
**Extensions:Packages of JS/HTML/CSS that add capability. The Core extension ships by default; the Web SDK extension is what actually loads alloy.js**, the library behind the Platform Web SDK.
**Data elements:**A dictionary of named values, mapped to query strings, cookies, JS variables, DOM content, that rules and extensions reference by name instead of hardcoding.
**Rules:**Event → conditions → exceptions → actions. When the event fires and every condition holds, the actions run in order, typically ending in a call that sends the Web SDK event.
All of that lives inside a property, and property-to-website is itself a many-to-many decision rather than a fixed 1:1. Adobe’s own guidance is to consolidate: if two sites share templates and need similar data, put them in one property rather than duplicating rules across separate ones. Split them into separate properties when the sites are genuinely different implementations (different extensions, different Analytics configurations) and you want independent control. A multi-brand org routinely runs both patterns at once, with a shared property for a family of near-identical microsites and dedicated properties for the flagship sites that need their own rule sets.
The Web SDK extension is where the collection layer actually gets wired to Experience Platform, and its settings matter more than the name suggests. Beyond the instance name (defaults to alloy) and edge domain (Adobe recommends a first-party CNAME; the shared third-party domain is fine for development, not production), the Datastreams section maps each of the three environments, production, staging, development, to a specific datastream ID. Every remaining stage in this map gets selected by that one per-environment mapping.
The datastream mapping lives per environment, not globally
It’s a common implementation mistake, regardless of how many sites or properties are involved: a staging build accidentally points at the production datastream ID (or vice versa), and nothing in the browser complains. The request is well-formed and gets accepted at the Edge Network no matter which environment picked which ID. Usually a data engineer or CJA admin is the one who notices, days later, when volume shows up in a dataset that shouldn’t have it.
Deployment itself is a build step, not a live edit. Changes get saved to a library, the library is built into the actual set of JS files that get deployed, and an embed code (a single <script> tag in the site's <head>) retrieves that build and runs it in the browser on every page load. Rules don't take effect until that build ships.
Inside the Tags engine, one page action

One rule firing is the entire collection layer’s job: decide something happened, gather the data for it, and hand one request to alloy.js addressed to a specific datastream.
03 The fork
Datastream: routing different events to different datasets
The datastream ID chosen back in the Web SDK extension is what the Edge Network actually looks up. A single Web SDK call, configured against one datastream, can fan out server-side to multiple Adobe solutions at once (Experience Platform, Analytics, Target, Audience Manager) without the browser making separate calls to each. I’ve covered that fan-out to services in full depth in a companion piece, so this section sticks to the one thing that piece doesn’t cover: how that same datastream routes different events to different datasets, which is where a multi-site implementation actually gets interesting.
Within a datastream’s Experience Platform service, the default event dataset isn’t the only one available. Add Event Dataset lets you attach one or more override datasets to the same datastream, and up to five routing rules decide which events go where. That cap is per service: five for Experience Platform, five for Analytics, five for Target, five for Audience Manager, five for Event Forwarding, all independent, all on the same datastream. Evaluation is first-match-wins, and Adobe’s own guidance is to write rules only for the exceptions, the subset of events that should deviate from the default, ordering the more specific rules first rather than enumerating every event type by hand.
That single mechanism is what makes real multi-site architectures possible without inventing anything exotic. A country-specific datastream can send everything to its own local dataset by default, then use one routing rule to redirect specific high-value events, an order, a profile update, into a shared global dataset instead. First-match-wins means a matched rule replaces the destination rather than adding to it. Adobe’s own documentation actually lists sending one event to multiple datasets in parallel as something this mechanism can’t do. Run that pattern across several regional datastreams, each keeping most events local and rerouting that same handful of event types elsewhere, and you end up with several independent local datasets plus one shared dataset fed by all of them. That’s still a genuine many-to-many between Datastream and Dataset: one datastream feeding more than one dataset across different event types, and one dataset receiving from more than one datastream. Fig 3 works through exactly this.
One practical ceiling to know about before multiplying event datasets per site: Adobe’s guardrail documentation recommends staying at or under 20 datasets using the XDM ExperienceEvent class per sandbox. It’s a soft limit, not a hard wall, but every additional per-site or per-region event dataset spends down that budget. It’s the same guardrail that governs Real-Time Customer Profile and segmentation elsewhere in the platform, so it’s worth checking before a fifth regional site each gets its own dataset by default.
04 Landing
Datasets: where the record lives, and the schema that gates it
A dataset is Experience Platform’s storage-and-management construct: a table bound to an XDM schema, with Catalog tracking the schema reference and record count as metadata. Incoming data gets validated against that structure, so incompatible values or types can produce validation or ingestion failures rather than being freely coerced into an arbitrary shape.
Two XDM classes cover almost everything that flows through this stage, and the distinction is the one a Connection will ask you to make again a stage later:

Schema evolution is a downstream risk, not just an upstream one
Adding a required field to an XDM schema after a dataset already holds data doesn’t retroactively touch existing records, but it can break any pipeline still built against the schema’s earlier shape, a datastream mapping, a batch ingest job. XDM schema evolution is additive-only: new minor versions must stay backward compatible, and once a schema has ingested data, its existing fields become non-editable. There’s no discrete old-version/new-version switch to fall back on the way Avro or Protobuf pipelines have. Treat schema changes as contract changes instead. Before tightening a field’s requirements or type, identify every ingestion path and downstream consumer that depends on the current shape.
05 Assembling the record
Connections: combining datasets into one reporting foundation
A CJA Connection is the admin construct that decides which Experience Platform datasets CJA is even allowed to see, and how they combine. It’s foundational in a literal sense: Connections “determine the availability of data (fields)” that any Data View built on top can later expose. At least one event (or summary) dataset is required. Profile/people datasets and lookup datasets are optional additions, all scoped to one sandbox with a configurable rolling data window controlling how much history is included.
How many event datasets one connection can even hold is gated by license tier, not just architecture. CJA Foundation is capped at one event dataset per connection; Select and above unlock multiple event datasets combined into one connection. Stitching method is gated the same way: field-based stitching from Select and up, graph-based stitching (the identity-graph lookup) from Prime and up. Confirm your SKU before designing a connection strategy that assumes you can combine everything into one.
A connection’s own job is dataset combination: deciding which datasets CJA can see together, in one sandbox. Stitching is the more specific piece underneath that, the identity-resolution processing that lets an event dataset and a profile dataset (or two event datasets from different channels) resolve to one person’s journey instead of two disconnected ID spaces. Worth keeping the two separate, and grounding the mechanics below in CJA’s own stitching documentation.† CJA supports two stitching mechanisms: field-based, a direct person-ID match across datasets, and graph-based, an identity-graph lookup by namespace, where the lexicographically first persistent ID is used if a namespace resolves to more than one. Either way, the resulting ID prefers the person ID when it’s non-blank, falls back to the persistent ID, and is only left blank when both are.
Stitched namespace values are lowercased internally
CJA normalizes namespace values during the union process, so
custEmailandcustemailresolve as the same identity for stitching purposes. Good to know before you spend an afternoon debugging why two datasets with differently-cased namespace labels stitched together anyway, or why a strict-casing rule somewhere else in your pipeline behaves differently from what CJA does at this step.
Once multiple event datasets are eligible for one connection, CJA doesn’t just stack them. It combines them into a single reporting dataset. Fields that share the same schema path merge into one column even across datasets; fields on different schema paths stay separate, even if they happen to share a name. Each dataset’s Person ID column, whatever it was called locally, merges into one unified ID column. Rows are then ordered by timestamp resolved to the millisecond, interleaving events from every combined dataset as if they’d always been one.
Combining datasets changes session boundaries, not just row count
When the same Person ID shows up in more than one combined dataset within a 30-minute window, CJA treats those hits as one session. That’s usually what you want (a web visit and an app open ten minutes apart, same person, one journey), but it’s a behavior change from analyzing the datasets separately, and it’s easy to miss until session counts move after a connection gets a new dataset added.
This article’s diagrams keep coming back to one point: a dataset isn’t owned by the connection it’s in. The same dataset can be included in more than one connection, which is exactly what Fig 3’s Global dataset does, referenced by both a Full-Funnel connection and a Regional Ops connection. The tradeoff is real, though. Keep related datasets together in one connection and you get unified cross-dataset analysis in a single Data View. Split the same or related datasets across separate connections and each team gets its own connection-level configuration (retention window, stitching method, access control on that connection), at the cost of not being able to analyze across those connections in one view. That independence sits at the connection layer, not the underlying dataset. A dataset itself is a sandbox-scoped AEP resource, so if two connections share one, they’re necessarily in the same sandbox, whatever CJA-side governance separates their use of it. Adobe’s own guidance here is to start lean: fewer datasets, one connection, and split out only what genuinely needs independent management, since every dataset combined into a connection still has to share a common identity scheme for stitching to hold together across the whole set.
One asymmetry to know about before you lean on lookup datasets across a multi-connection setup: a lookup dataset can only be associated with one dataset within a connection. There’s no fan-out of a single shared lookup table (a product catalog, a store directory) across multiple datasets in the same connection. If two datasets both need the same reference data, the lookup has to be attached twice, once per dataset.
What a Connection actually assembles

A Connection doesn’t move or copy data. It defines which lake datasets CJA reads and how their identifiers are reconciled into one person view. The same source dataset can feed more than one Connection at once.
06 Naming things
Data Views: the semantic layer analysts actually see
A Data View sits directly on top of one Connection and is where raw schema fields become the dimensions and metrics an analyst drags into Workspace. It can hold up to 5,000 dimensions and 5,000 metrics, and none of that requires touching the underlying XDM schema. A Data View can convert a field’s component type (turn a numeric field into a dimension, or a raw field into a metric), and it can derive several distinct metrics from the same schema field with different attribution models or lookback windows: “purchase revenue, last-touch, 30-day” and “purchase revenue, linear, 90-day” can coexist as two separate components built from one field. Every component gets a friendly name, a stable component ID for API use, and formatting (currency, decimals, percentage), plus value-level filtering to exclude specific values.

Retroactive and non-destructive cuts both ways
Adobe’s own framing for Data View edits is “retroactive and non-destructive.” Nothing in the source data is altered, which is genuinely useful for correcting a metric definition after the fact. But it also means a definition change silently recalculates every existing project built on that Data View, with no row-level version history baked into the UI. If more than one analyst relies on a shared Data View, keep a changelog somewhere outside it.
This is the one stage in the pipeline I’ve already covered end to end elsewhere. What a Data View shares with a dbt/Cube/LookML-style semantic layer, the BI Extension’s SQL access over the same definitions, and where the analogy actually breaks down, is the subject of a companion piece: Customer Journey Analytics Data Views Are Effectively a Semantic Layer. Worth the full read if Stage 6 is the one you actually own.
07 Where people actually work
Projects: panels, not one canvas
A Workspace project is organized into panels, not one flat report. CJA offers eleven panel types, among them Blank, Freeform (unlimited tables and visualizations), Attribution (compare attribution models side by side), Experimentation (test experience/messaging variants), several media panels (average minute audience, concurrent viewers, playback time), Next/Previous Item (path analysis), and Quick Insights (auto-generated tables and charts). Components (metrics, dimensions, segments, date ranges) get dragged in from a curated left-rail list, and admins control exactly which components a given user group even sees there.
The detail worth knowing before you build anything complex: each panel is bound to one Data View, chosen independently. Switching a panel’s Data View can silently break any component in it that doesn’t exist in the new one, and nothing stops a single project from mixing panels pointed at different Data Views, even different Connections, side by side.
One project can quietly compare two semantic layers
Because the Data View is a per-panel choice, it’s entirely possible to put two panels in one project that look like they’re describing the same metric but were actually built from Data Views with different attribution models or lookback windows underneath. The panel headers won’t tell you that by default, so check the Data View each panel is bound to before comparing numbers across them.
08 Putting it together
The whole path, and the handoff worth double-checking at each stage
Every stage above owns exactly one kind of failure that’s invisible from the neighboring stage. Laid out together, it becomes clear that debugging almost always means figuring out which of three people to loop in, not which line of code to fix:

- Treat the three surfaces as three owners, not one pipelineBefore debugging “why isn’t this event showing up,” identify whether the gap is in Tags, Experience Platform, or CJA. That alone usually tells you who needs to be in the room.
- Check the datastream-to-environment mapping first when numbers look wrongIt’s the single easiest hop to misconfigure, and the hardest one to notice from inside CJA, since nothing downstream flags it as an error.
- Write datastream routing rules as exceptions, not an enumerationRoute the default case implicitly and add rules only for what should deviate, like a specific event type rerouted to a shared dataset, ordering the more specific rules first. You get five per service, per datastream; don’t spend them on cases the default already covers.
- Decide your Record vs. Time-series split before building the Connection, not afterThe Individual Profile / ExperienceEvent classification at the schema stage is the profile-dataset / event-dataset choice a Connection asks you to make a stage later.
- Decide deliberately whether a dataset is shared or isolated across connectionsReusing one dataset in multiple connections keeps a cross-region or cross-brand view alive while preserving one shared source. Keeping datasets in separate connections instead gives each team independent connection-level configuration and governance (retention window, stitching method, access control) over its own view. Both are valid; the mistake is not knowing which one you actually did.
- Document Data View changes somewhere outside the UIRetroactive, non-destructive edits mean there’s no built-in audit trail for “why did last quarter’s number change.” A shared changelog is the cheapest fix.
- Pin a panel’s Data View deliberately in any project comparing numbers across sourcesDon’t let two panels in the same project quietly run on two different semantic layers and assume the numbers are comparable.
Seven stages, three administrative surfaces, and six handoffs where context can disappear. What most diagrams skip is everything that happens at each one: identity attaching to an event, an event settling into a schema, a schema getting a friendlier name, and a name finally becoming a number in a chart.
None of this is a design flaw. Collection, storage, and analysis were never going to share one admin surface, and splitting them across three administrative surfaces is exactly what lets a tag developer, a platform admin, and an analyst each work without stepping on the other two. The map is only useful if you know which of the seven stages you’re actually standing in when something looks wrong.
References
- Adobe Experience League — Tags overview, Data elements, Rules, Quickstart guide (source of the library/build/embed-code deployment flow), Companies and properties (source of the shared-vs-dedicated property guidance)
- Adobe Experience League — Configure the Web SDK tag extension (source of the instance name, edge domain, and per-environment datastream settings)
- Adobe Experience League — Create and configure datastreams (source of the Add Event Dataset mechanic and the 5-rules-per-service cap), Dynamic datastream configuration patterns (source of the first-match-wins, exceptions-only routing guidance), Real-Time Customer Profile & segmentation guardrails (source of the 20-dataset ExperienceEvent soft guardrail)
- Adobe Experience League — Datasets overview, XDM schema composition (source of the Individual Profile / ExperienceEvent class distinction)
- Adobe Experience League — Connections overview, Stitching FAQ (source of the field-based/graph-based stitching mechanics, SKU gating, and the lowercase-namespace behavior), Combined event datasets (source of the schema-path merge, Person ID harmonization, and 30-minute session-boundary behavior), Best practices for setting up connections in CJA (source of the shared-vs-isolated dataset tradeoff and the “start lean” guidance). † Adobe’s own material isn’t fully consistent on where “stitching” lives — the Best Practices perspective frames identity resolution as an AEP Identity Service job with CJA merely consuming already-stitched identities, while the Stitching FAQ and Connections overview describe field-based and graph-based stitching as mechanics CJA itself performs at the connection level. This piece follows the latter, since it’s what determines whether two specific datasets in your connection actually resolve to one person.
- Adobe Experience League — Data Views overview (source of the 5,000-component ceiling and the retroactive/non-destructive framing)
- Adobe Experience League — Panels in Analysis Workspace, Use components in a project
- Companion pieces — The Datastream Is Not a Pipe (Stage 3, in full), CJA Data Views Are Effectively a Semantic Layer (Stage 6, in full), RTCDP, Target, and CJA Aren’t Three Products (what happens after Stage 7, when CJA publishes an audience back out), Copy, Mirror, or Federate (how data reaches the lake through non-Web-SDK paths)
Written August 2026 · Reflects Adobe Tags / Experience Platform / CJA behavior as documented at that date. UI section names, component ceilings, and default values are the details most likely to change — verify against current Adobe docs and your own tenant before designing around them.
메타데이터
- post_id
- c6852fc36fb7
- slug
- the-real-cja-pipeline-seven-stages-three-consoles-six-handoffs-c6852fc36fb7
- url
- https://medium.com/tech-learnings/the-real-cja-pipeline-seven-stages-three-consoles-six-handoffs-c6852fc36fb7
- canonical_url
- https://medium.com/tech-learnings/the-real-cja-pipeline-seven-stages-three-consoles-six-handoffs-c6852fc36fb7
- author_url
- https://medium.com/@techforum
- status
- ok
- fetched_at
- 2026-09-03 18:28:56