Logo

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:Emotion or ujm: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:
  • Transitions leaving the CallStep in the caller Journey that use label SHOULD use values listed in exitMap.label.
  • If exitMap is omitted, a single unlabeled return path is assumed, and callers SHOULD use unlabeled transitions or a single default label.
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.actor SHOULD be pseudonymous and SHOULD NOT directly embed personal data (for example, names, email addresses, phone numbers, or raw device identifiers).
  • Metric and StepStatistics resources 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 name and value, and MAY include unit, method, and sampleSize.

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:
  • the Journey identified by the parent JourneyRun.journey, or
  • a callee Journey referenced via a CallStep in that Journey.
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 E be a StepEvent with journeyRun = R, and let J = R.journey. For each Phase or Session in J whose StepRef step equals E.step, E is 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 step is a CallStep MAY reference a nested JourneyRun via calleeRun. 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. in type expand to their ujm: 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 Journey that uses a CallStep to invoke a nested callee Journey, and
  • runtime JourneyRun and StepEvent resources, including a nested calleeRun,

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.

JSON

  {
  "$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.