User Journey Mapping
ujm
This is an Editor's Draft. It may be updated, replaced, or obsoleted at any time.
Abstract
This specification defines UJM (User Journey Mapping), a vocabulary, data model, and JSON-LD/JSON serialization for describing user journeys as graphs of Steps connected by Transitions, optionally grouped into Phases and Sessions. It clearly separates design-time journey definitions from runtime executions and analytics, so structural models are not conflated with observed data.
The specification provides:
- A normative design-time model for Journey-level structures.
- A normative execution and analytics model (JourneyRun, StepEvent, StepStatistics).
- A JSON-LD context for semantic interoperability.
- An illustrative JSON Schema aligned with the design/runtime split.
Conformance
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC2119.
Sections marked as “normative” are prescriptive. Other sections are informative.
This specification uses IRIs for identifiers and assumes JSON-LD as the
canonical serialization. When examples or schemas use compact terms such as
"Journey" or "StepEvent", they are intended to be
interpreted via the JSON-LD context in JSON-LD, where
they expand to IRIs in the
https://www.openuji.org/ns/ujm# namespace (for example,
"Journey" → ujm:Journey).
Requirements stated using ujm:ClassName apply to JSON
representations whose type is the compact term
"ClassName" under that context. In this specification,
type MUST be a single string value, not an array. For subclass
relationships defined in the UJM vocabulary (for example,
ujm:CallStep as a subclass of ujm:Step),
requirements on the superclass also apply to JSON representations whose
type is the subclass term only. Some structural helper objects
(such as inline StepRef values) MAY omit a
type and are governed by the constraints of their containing
resources.
UUID-based identifiers used as id values SHOULD be expressed as
IRIs (for example, using the urn:uuid: scheme) so they are
valid JSON-LD @id values.
Data Model (Design-time)
Unless explicitly stated otherwise, all classes defined in this section are design-time structures:
- They describe intended journeys, groupings, channels, and touchpoints.
- They MUST NOT contain per-execution telemetry such as event timestamps, user or session identifiers, raw event logs, or measured metrics.
-
They MUST NOT contain instances of
ujm:Emotionorujm:Metric. Emotions and metrics are reserved for runtime and analytics resources. - Runtime information MUST be represented using the Execution and Analytics Model defined in Execution and Analytics Model.
Core Classes
ujm:Journey
A Journey is a design-time definition of a user journey: a
graph of Steps and Transitions, optionally grouped by
Phases and Sessions. Concrete executions are represented
by JourneyRun and StepEvent and
MUST NOT be encoded directly in a Journey.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of the journey definition. |
type |
1 | MUST be ujm:Journey. |
title |
1 | Human-readable title. |
version |
0..1 |
Version identifier of this Journey definition (e.g.,
"1.0.0"). Producers using version SHOULD
update it when structural changes (steps, transitions, phases,
sessions, CallSteps) materially affect interpretation or
comparison. No specific versioning scheme is mandated.
|
goal |
0..1 | Free-text description of the journey’s purpose. |
created, modified |
0..1 each | ISO 8601 date-time for definition lifecycle, not executions. |
personas |
0..* | Array of Persona IRIs relevant to this journey. |
tags |
0..* | Array of classification strings. |
steps |
1..* | Array of Step and/or CallStep definitions that belong to this Journey. Step identifiers MUST be unique within the Journey. |
transitions |
0..* | Array of Transitions connecting steps within this Journey only. |
phases, sessions |
0..* | Arrays of Phase and Session groupings whose members are StepRefs referencing steps in this Journey. |
start, end |
0..1 each |
IRIs of designated initial and terminal Steps within this
Journey. A Journey intended to be a callee of a
CallStep MUST define start.
|
touchpoints |
0..* |
Array of Touchpoint definitions that are declared in-scope of this
Journey. Step-level touchpoint values SHOULD reference a
member of this array when present, unless an external URL or externally
managed Touchpoint is intended. Touchpoint identifiers SHOULD be unique
within the Journey.
|
channelDefinitions |
0..* |
Array of ChannelDefinition resources providing human-readable
metadata for custom or profile-specific channels used in this Journey.
Step-level channel values MAY reference these definitions by
IRI. This property does not replace the controlled ujm:
channel terms.
|
ujm:Step
A Step is a design-time node in a Journey,
describing intent, semantics, and placement in the journey graph. It
does not embed runtime telemetry; observed occurrences are modeled via
StepEvent.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of the step. |
type |
1 |
MUST be ujm:Step for resources that are instances of
ujm:Step and not of a more specific subclass.
Subclasses such as ujm:CallStep MUST use their own
class term in type instead of "Step".
|
name |
1 | Human-readable label. |
description |
0..1 | Free-text description. |
touchpoint |
0..1 |
IRI of a Touchpoint or external URL. If the containing Journey
declares touchpoints, a Step that references a Touchpoint
defined within the Journey SHOULD use one of those Touchpoint IRIs.
|
channel |
0..1 |
Controlled design-time term via @vocab (e.g.,
ujm:Web, ujm:MobileApp). For extensions, the
value MAY be a full IRI identifying a ChannelDefinition declared
in Journey.channelDefinitions or an external channel registry.
|
persona |
0..1 | IRI of a primary Persona for which this step is modeled. |
preconditions, postconditions |
0..* | Free-text design assumptions and expected outcomes. |
ujm:CallStep
A CallStep is a specialized Step that composes
another design-time Journey. It provides structural composition
at design time only. A CallStep inherits all
Step properties and can be included in Phase/Session
groupings via StepRef.
| Property | Cardinality | Constraints |
|---|---|---|
type |
1 |
MUST be ujm:CallStep. CallStep instances MUST NOT
also declare ujm:Step in type;
Step-level constraints apply via the subclass relationship
described in this specification.
|
calls |
1 | IRI of the callee Journey. MUST reference an existing Journey definition. |
entry |
0..1 |
IRI of a Step in the callee Journey. If provided, it
overrides the callee Journey’s start. If absent,
consumers MUST use the callee Journey’s start.
|
exitMap |
0..* |
Array of mappings from callee terminal Steps to labels in
the caller Journey, e.g.
{"calleeEnd":"https://example.org/journey/checkout#success","label":"success"}. The calleeEnd value MUST be the IRI of a
Step in the callee Journey identified by
calls. It identifies the terminal Step in the callee
for which the mapping applies. For deterministic composition:
|
parameters |
0..1 | Design-time description of inputs made available to the callee Journey (e.g., keys, defaults, or binding expressions). This is a structural contract and MUST NOT contain per-execution telemetry. |
returns |
0..1 |
Design-time description of expected outputs or result shape from
the callee Journey (e.g.,
{"status":["success","failure"]}). It documents
semantics only and MUST NOT contain concrete runtime results.
Consumers SHOULD ignore unknown fields.
|
At runtime, executions of a CallStep are represented as
StepEvents within a JourneyRun. The
calleeRun property on StepEvent links to nested
JourneyRuns; all concrete values belong to the execution model.
ujm:Transition
A Transition connects two Steps within the same
Journey. It is a design-time construct and MUST NOT contain
runtime telemetry. The from and to steps MUST
be distinct; self-transitions where from and
to identify the same Step are NOT allowed.
| Property | Cardinality | Notes |
|---|---|---|
id |
0..1 | IRI of the transition (recommended if transitions need stable references). |
type |
1 | MUST be ujm:Transition. |
from, to |
1 each |
IRIs of Steps defined in the same containing
Journey. The from and to values
MUST refer to two distinct Steps (i.e., from and
to MUST NOT be equal).
|
label |
0..1 |
Design-time outcome label (e.g., "success",
"retry"). When used with CallStep, SHOULD
align with CallStep.exitMap.label.
|
pattern |
0..1 | IRI of a TransitionPattern describing intended behavior or UX of this transition. |
ujm:TransitionPattern
A TransitionPattern is a reusable design-time description
of how a Transition should be experienced or implemented (e.g.,
visual behavior, handoff style, interaction pattern). It does not
contain event-level telemetry.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of the pattern. |
type |
1 | MUST be ujm:TransitionPattern. |
name |
1 | Human-readable name. |
description |
0..1 | Free-text description of intended behavior. |
Runtime resources such as StepEvent or aggregates MAY reference a
TransitionPattern to compare outcomes across patterns
(e.g., A/B tests, accessibility variants).
ujm:Phase, ujm:Session, and ujm:StepRef
Phase and Session are design-time groupings of
Steps using members of type StepRef. They define
structural relationships only and MUST NOT embed runtime data. A given
Step MAY belong to multiple Phases and/or Sessions.
ujm:Phase
A Phase represents a conceptual stage of journey progress
(e.g., Awareness, Consideration, Onboarding, Retention).
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of the phase. |
type |
1 | MUST be ujm:Phase. |
name |
1 | Human-readable label. |
members |
1..* | Array of StepRef referencing steps in the same Journey. |
ujm:Session
A Session is a design-time grouping of steps that are
expected to occur within a coherent interaction episode or lifecycle
slice (e.g., a login session, onboarding call, support interaction).
Sessions are structural expectations, not raw technical sessions or
logs.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of the session grouping. |
type |
1 | MUST be ujm:Session. |
name |
0..1 | Human-readable label. |
members |
1..* | Array of StepRef referencing steps in the same Journey. |
ujm:StepRef
A StepRef references a Step within a grouping and
may specify an ordering.
| Property | Cardinality | Notes |
|---|---|---|
step |
1 | IRI of the referenced Step, which MUST belong to the same Journey as the containing Phase/Session. |
order |
0..1 | Sequence index (number or string) within the grouping; ordering is advisory. |
StepRef is typically used as an inline structural object
within Phase and Session resources and does not itself
carry an id or type. Its semantics are
determined by the step it references and the containing
grouping resource.
Summary:
- Phase – groups steps by conceptual journey progress.
- Session – groups steps by expected lifecycle or interaction episode.
- Both are design-time only; runtime membership is derived from StepEvents.
ujm:Channel
Channel is a controlled design-time term identifying
interaction channels (e.g., ujm:Web,
ujm:MobileApp, ujm:Phone,
ujm:InPerson, ujm:Email,
ujm:SMS). Vocabularies MAY be extended.
For custom channels that need human-readable metadata, journeys MAY declare
ChannelDefinition resources and reference them by IRI from
Step.channel.
ujm:Touchpoint
A Touchpoint is a design-time description of a user
interaction surface or location.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | MUST be an IRI. |
type |
1 | MUST be ujm:Touchpoint. |
name |
1 | Human-readable name. |
uri |
0..1 | Associated URL or resource identifier. |
location |
0..1 | Free-text or external reference to a physical/virtual location. |
owner |
0..1 | Free-text or IRI of responsible party. |
ujm:ChannelDefinition
A ChannelDefinition is a design-time resource that provides
human-readable metadata for a channel used in a Journey. It enables
profile-specific or domain-specific channels while preserving the existing
controlled-term model for Step.channel.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | MUST be an IRI. |
type |
1 | MUST be ujm:ChannelDefinition. |
name |
1 | Human-readable name for the channel. |
description |
0..1 | Free-text description. |
uri |
0..1 | Associated URL or documentation page for the channel definition. |
owner |
0..1 | Free-text or IRI of responsible party. |
ujm:Persona
Persona is an imported design-time archetype. Journeys and
Steps MAY reference Personas to describe intended audiences or segments.
Persona resources themselves MUST NOT embed runtime
telemetry, ujm:Emotion, or
ujm:Metric instances.
Execution and Analytics Model (Runtime)
This section defines runtime classes that represent concrete executions of design-time Journey definitions, and aggregated analytics derived from those executions. Runtime resources MAY reference design-time resources, but design-time resources MUST NOT embed runtime telemetry.
Privacy and Personal Data
This specification is designed primarily for structural journey models and aggregated analytics. It does not require the collection of any personal data.
-
Identifiers used in runtime resources such as
JourneyRun.actorSHOULD be pseudonymous and SHOULD NOT directly embed personal data (for example, names, email addresses, phone numbers, or raw device identifiers). -
MetricandStepStatisticsresources are intended to carry aggregated measurements. They MUST NOT be used to store raw personal attributes or per-person identifiers. - Implementations that process personal data remain responsible for complying with applicable privacy and data-protection regulations; such policies are out of scope for this specification.
ujm:Emotion and ujm:Metric
Emotion and Metric are imported runtime and
analytics constructs. They are used exclusively on runtime and analytics
resources such as StepEvent and StepStatistics.
-
Emotion — describes observed or inferred emotional
states and MAY include supporting
evidence. -
Metric — describes measured values (e.g., status,
conversion, duration), with at least
nameandvalue, and MAY includeunit,method, andsampleSize.
Design-time resources (e.g., Journey, Step, Phase, Session, Touchpoint,
Persona) MUST NOT include ujm:Emotion or
ujm:Metric instances.
Design assumptions and targets SHOULD be expressed as free-text or
profile-specific extensions instead.
ujm:JourneyRun
A JourneyRun represents a single concrete execution (by a
user, technical session, account, or cohort) of a design-time
Journey.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of this execution (e.g., urn:uuid:...). |
type |
1 | MUST be ujm:JourneyRun. |
journey |
1 | IRI of the design-time Journey this run instantiates. All StepEvent.step values in this run MUST be consistent with this Journey and any nested callees referenced via CallStep. |
actor |
0..1 | IRI of an abstract subject associated with this run (for example, a pseudonymous account key, a technical session ID, or a Persona). |
started, ended |
0..1 each | ISO 8601 date-times bounding this run. |
stepEvents |
0..* |
Array of references to, or embedded representations of,
StepEvents belonging to this run. For analytical purposes,
the sequence of events in a JourneyRun MUST be derived from
event-level properties such as StepEvent.order and/or
timestamp. The JSON array order (and any JSON-LD
container semantics) is not normative for defining the execution
order of steps.
|
tags |
0..* | Run-level labels (e.g., experiment variant, segment, channel bundle). |
ujm:StepEvent
A StepEvent represents one concrete occurrence of a
design-time Step within a JourneyRun. It is the canonical
location for recording observed timestamps, durations, metrics, and
emotions.
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of the event (e.g., urn:uuid:...). |
type |
1 | MUST be ujm:StepEvent. |
step |
1 |
IRI of the design-time Step instantiated by this event. The
referenced Step MUST belong to:
|
journeyRun |
1 | IRI of the containing JourneyRun. |
timestamp |
0..1 | ISO 8601 date-time when the step occurrence started (or was observed). |
duration |
0..1 | ISO 8601 duration for this occurrence. |
emotions |
0..* | Array of observed Emotion values associated with this occurrence. |
metrics |
0..* |
Array of observed Metric values (e.g.,
{"name":"status","value":"success"}).
|
order |
0..1 | Sequence index (number or string) of this event within its JourneyRun. Ordering is profile-specific. |
calleeRun |
0..1 |
For events whose step is a CallStep, IRI of a
nested JourneyRun representing execution of the callee
Journey. If present, calleeRun.journey MUST equal the
CallStep.calls Journey.
|
ujm:StepStatistics
A StepStatistics resource captures aggregated metrics or
emotion distributions for a given design-time Step over a defined
set of StepEvents (e.g., a time window, cohort, or experiment).
| Property | Cardinality | Notes |
|---|---|---|
id |
1 | IRI of this statistics resource. |
type |
1 | MUST be ujm:StepStatistics. |
step |
1 | IRI of the design-time Step being summarized. |
journey |
0..1 | IRI of the Journey context, if applicable (e.g., when a Step appears in multiple Journeys). |
scope |
0..1 | String or object describing the aggregation scope (e.g., time window, cohort, experiment). |
metrics |
0..* | Array of aggregated Metric values (e.g., conversion rate, average duration). |
emotions |
0..* | Array or distribution of aggregated Emotion values. |
sampleSize |
0..1 | Total number of contributing StepEvents. |
derivedFrom |
0..* | IRIs or identifiers of underlying datasets, JourneyRuns, or queries from which these statistics were computed. |
Relationship to Phases, Sessions, and CallStep
-
Phases and Sessions.
Let
Ebe a StepEvent withjourneyRun = R, and letJ = R.journey. For each Phase or Session inJwhose StepRefstepequalsE.step,Eis considered a member of that Phase/Session for analytical purposes. A StepEvent MAY thus belong to multiple Phases and Sessions. Aggregated metrics for Phases/Sessions SHOULD be computed from their member StepEvents and MAY be exposed via additional statistics resources. -
CallStep.
At design time, a CallStep is a Step that references a
callee Journey. At runtime, a StepEvent whose
stepis a CallStep MAY reference a nested JourneyRun viacalleeRun. Statistics for the CallStep MAY be derived from the corresponding nested JourneyRuns and exposed as StepStatistics for that CallStep.
JSON-LD
This section provides a normative JSON-LD context for compact JSON representations of UJM resources. Implementations MAY extend this context but MUST preserve the semantics of terms defined here.
With this context:
-
"Journey","Step","JourneyRun", etc. intypeexpand to theirujm:IRIs via@vocab. -
Properties declared with
"@type": "@id"are interpreted as IRIs. -
The same compact JSON documents can serve as both JSON and RDF, ensuring
that normative requirements on
ujm:*classes apply directly.
Implementation Profile Example
This section provides a concise end-to-end example showing:
-
a design-time
Journeythat uses aCallStepto invoke a nested calleeJourney, and -
runtime
JourneyRunandStepEventresources, including a nestedcalleeRun,
The example is expressed in compact JSON-LD using the context defined in JSON-LD and is constructed to validate against the illustrative JSON Schema in Appendix A.
Appendix A: JSON Schema (Illustrative Extract)
This appendix sketches JSON Schema fragments aligned with the design-time /
runtime split. The schema targets the compact JSON form used together with
the JSON-LD context in
JSON-LD. Type values like "Step" or
"JourneyRun" are understood to expand via
@vocab to the corresponding ujm: IRIs.
{
"$defs": {
"Journey": {
"type": "object",
"required": ["id", "type", "title", "steps"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": { "const": "Journey" },
"title": { "type": "string" },
"version": { "type": "string" },
"goal": { "type": "string" },
"created": { "type": "string", "format": "date-time" },
"modified": { "type": "string", "format": "date-time" },
"personas": {
"type": "array",
"items": { "type": "string", "format": "uri" }
},
"tags": {
"type": "array",
"items": { "type": "string" }
},
"steps": {
"type": "array",
"minItems": 1,
"items": {
"oneOf": [
{ "$ref": "#/$defs/Step" },
{ "$ref": "#/$defs/CallStep" }
]
}
},
"transitions": {
"type": "array",
"items": { "$ref": "#/$defs/Transition" }
},
"phases": {
"type": "array",
"items": { "$ref": "#/$defs/Phase" }
},
"sessions": {
"type": "array",
"items": { "$ref": "#/$defs/Session" }
},
"start": { "type": "string", "format": "uri" },
"end": { "type": "string", "format": "uri" },
"touchpoints": {
"type": "array",
"items": { "$ref": "#/$defs/Touchpoint" }
},
"channelDefinitions": {
"type": "array",
"items": { "$ref": "#/$defs/ChannelDefinition" }
}
}
},
"Touchpoint": {
"type": "object",
"required": ["id", "type", "name"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": { "const": "Touchpoint" },
"name": { "type": "string" },
"uri": { "type": "string", "format": "uri" },
"location": { "type": "string" },
"owner": { "type": "string" },
"description": { "type": "string" }
}
},
"ChannelDefinition": {
"type": "object",
"required": ["id", "type", "name"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": { "const": "ChannelDefinition" },
"name": { "type": "string" },
"description": { "type": "string" },
"uri": { "type": "string", "format": "uri" },
"owner": { "type": "string" }
}
},
"Step": {
"type": "object",
"required": ["id", "type", "name"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": { "const": "Step" },
"name": { "type": "string" },
"description": { "type": "string" },
"touchpoint": { "type": "string", "format": "uri" },
"channel": { "type": "string" },
"persona": { "type": "string", "format": "uri" },
"preconditions": {
"type": "array",
"items": { "type": "string" }
},
"postconditions": {
"type": "array",
"items": { "type": "string" }
}
}
},
"CallStep": {
"type": "object",
"required": ["id", "type", "name", "calls"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": { "const": "CallStep" },
"name": { "type": "string" },
"description": { "type": "string" },
"touchpoint": { "type": "string", "format": "uri" },
"channel": { "type": "string" },
"persona": { "type": "string", "format": "uri" },
"preconditions": {
"type": "array",
"items": { "type": "string" }
},
"postconditions": {
"type": "array",
"items": { "type": "string" }
},
"calls": { "type": "string", "format": "uri" },
"entry": { "type": "string", "format": "uri" },
"exitMap": {
"type": "array",
"items": {
"type": "object",
"required": ["calleeEnd", "label"],
"properties": {
"calleeEnd": { "type": "string", "format": "uri" },
"label": { "type": "string" }
}
}
},
"parameters": { "type": "object" },
"returns": { "type": "object" }
}
},
"JourneyRun": {
"type": "object",
"required": ["id", "type", "journey"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": {
"const": "JourneyRun"
},
"journey": { "type": "string", "format": "uri" },
"actor": { "type": "string", "format": "uri" },
"started": { "type": "string", "format": "date-time" },
"ended": { "type": "string", "format": "date-time" },
"stepEvents": {
"type": "array",
"items": {
"oneOf": [
{ "type": "string", "format": "uri" },
{ "$ref": "#/$defs/StepEvent" }
]
}
},
"tags": {
"type": "array",
"items": { "type": "string" }
}
}
},
"StepEvent": {
"type": "object",
"required": ["id", "type", "step", "journeyRun"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": {
"const": "StepEvent"
},
"step": { "type": "string", "format": "uri" },
"journeyRun": { "type": "string", "format": "uri" },
"timestamp": { "type": "string", "format": "date-time" },
"duration": { "type": "string" },
"emotions": {
"type": "array",
"items": { "$ref": "#/$defs/Emotion" }
},
"metrics": {
"type": "array",
"items": { "$ref": "#/$defs/Metric" }
},
"order": {
"oneOf": [
{ "type": "number" },
{ "type": "string" }
]
},
"calleeRun": { "type": "string", "format": "uri" }
}
},
"StepStatistics": {
"type": "object",
"required": ["id", "type", "step"],
"properties": {
"id": { "type": "string", "format": "uri" },
"type": {
"const": "StepStatistics"
},
"step": { "type": "string", "format": "uri" },
"journey": { "type": "string", "format": "uri" },
"scope": {"oneOf": [
{ "type": "string" },
{ "type": "object" }
]},
"metrics": {
"type": "array",
"items": { "$ref": "#/$defs/Metric" }
},
"emotions": {
"type": "array",
"items": { "$ref": "#/$defs/Emotion" }
},
"sampleSize": {
"type": "integer",
"minimum": 0
},
"derivedFrom": {
"type": "array",
"items": { "type": "string" }
}
}
}
/* Emotion, Metric, Journey, Phase, Session, StepRef, Touchpoint, etc.,
are omitted here for brevity but follow the normative model. */
}
}
These fragments are non-normative and intentionally permissive. They do not capture all normative constraints (such as the prohibition on Emotion/Metric in design-time resources or all IRI requirements). In case of conflict, the normative model and JSON-LD semantics take precedence.