Events
Event Files
Section titled “Event Files”Event files define individual analytics events with taxonomy metadata and payload schemas.
Example
Section titled “Example”# yaml-language-server: $schema=https://opentp.dev/schemas/latest/event.schema.jsonopentp: 2026-01
event: key: auth::login_button_click
lifecycle: status: active
taxonomy: action: User clicks the login button
payload: all: current: "1.0.0" "1.0.0": meta: changes: Initial version schema: event_name: value: login_button_click x-opentp: role: constant auth_method: type: string enum: [email, google, github] required: trueReference
Section titled “Reference”Root Fields
Section titled “Root Fields”| Field | Type | Required | Description |
|---|---|---|---|
opentp | string | Yes | Format version |
event | object | Yes | Event definition |
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique event identifier (opaque string) |
lifecycle | object | No | Event status |
taxonomy | object | Yes | Metadata fields |
payload | object | Yes | Analytics data |
aliases | array | No | Previous event keys |
ignore | array | No | Skip specific validations |
event.key
Section titled “event.key”event.key is an opaque string identifier that must be unique within the tracking plan.
Portable constraints for keys can be configured in opentp.yaml via spec.events.key (constraints only).
Tooling may also support key generation via spec.events.x-opentp.keygen (extension).
event.lifecycle
Section titled “event.lifecycle”lifecycle: status: active # active | deprecated | draft| Status | Description |
|---|---|
active | Event is in production |
deprecated | Event is being phased out |
draft | Event is being developed |
Lifecycle fields:
| Field | Type | Description |
|---|---|---|
status | string | active, deprecated, or draft |
deprecatedAt | string (date) | Date when the event was deprecated (ISO 8601) |
deprecatedReason | string | Reason for deprecation |
replacedBy | string | Key of the replacement event |
event.taxonomy
Section titled “event.taxonomy”Metadata fields defined in your opentp.yaml under spec.events.taxonomy:
taxonomy: action: User clicks the login buttonIf a taxonomy field is present in spec.paths.events.template (for example {area}/{event}.yaml),
its value is extracted from the event file path and does not need to be duplicated in event.taxonomy.
event.payload
Section titled “event.payload”payload supports two forms:
- Implicit
allselector (simplest):
payload: schema: event_name: value: login_click- Map form (
payload.<key>) wherekeyis either:
- a selector id defined in
opentp.yamlunderspec.events.payload.targets, or - a direct target id listed in
spec.events.payload.targets.all.
payload: mobile: schema: event_name: value: login_click web: schema: event_name: value: login_clickResolution rules (no overlaps):
- Each
payload.<key>expands to one or more target IDs. - In a single event file, a target ID must be covered at most once (no overlaps between selectors/targets). If a target is matched multiple times, tooling should treat it as an error (ambiguous payload definition).
- If you want one payload definition to apply to every target, use the implicit form (
payload.schema/payload.current), which applies topayload.targets.all.
Canonical resolution algorithm (selectors → targets)
Section titled “Canonical resolution algorithm (selectors → targets)”Given an event file and its corresponding opentp.yaml:
- Read
spec.events.payload.targets.allis required and reserved. - Collect
allTargets = spec.events.payload.targets.all(canonical list of target IDs). - If
event.payloadis in implicit form (payload.schemaorpayload.currentat the top level), apply that payload definition to every target inallTargetsand stop. - Otherwise, treat
event.payloadas map form and iterate its keys. - For each
payloadKey:- If
payloadKeyexists inspec.events.payload.targets, expand it to that selector’s list of target IDs. - Else if
payloadKeyis present inallTargets, treat it as a direct target ID. - Else: error (unknown selector/target).
- If
- Ensure each target ID is assigned at most once across all payload keys. If any target appears more than once: error (ambiguous overlap).
For each selector/target payload definition you can choose:
- Unversioned:
{ schema, meta? } - Versioned:
{ current, <versions>, <aliases> }
Unversioned payload
Section titled “Unversioned payload”payload: schema: event_name: value: login_clickVersioned payload
Section titled “Versioned payload”payload: all: current: "1.1.0" "1.0.0": schema: event_name: value: login_click "1.1.0": $ref: "1.0.0" meta: changes: - Added auth_method schema: auth_method: type: string enum: [email, google]Aliases (tags) and $ref:
- Version keys are entries with object values (payload versions); aliases/tags are entries with string values pointing to other keys.
currentmay be a version key or an alias/tag; it must resolve to a version key.$refderives schema from another version (same payload key orotherPayloadKey::versionKey). See Semantics for the full resolution rules.
Effective payload schema (merge/precedence)
Section titled “Effective payload schema (merge/precedence)”See Semantics for the normative merge/precedence and conflict rules.
Do / Don’t
Section titled “Do / Don’t”Do: use implicit form when the payload is the same for every target
payload: schema: event_name: value: login_clickDo: use disjoint selectors (or direct target IDs) when targets differ
payload: ios-ga: schema: ios_extra: type: string required: false android-ga: schema: android_extra: type: string required: falseDon’t: define overlapping payload keys
# Ambiguous: ios/android are covered by both keyspayload: all: schema: {} mobile: schema: {}Payload field definition
Section titled “Payload field definition”Payload fields use the shared Field schema (field.schema.json).
Common properties:
| Field | Description |
|---|---|
name | Logical or code-facing field name. The YAML key remains the canonical payload field key |
title | Human-readable field title |
description | Field description |
type | string, number, integer, boolean, or array |
required | Whether the field must be present in payload |
valueRequired | Whether this field must define a fixed value in the effective schema (enforced by tooling; independent of required) |
value | Fixed value (constant) |
example | Example value for documentation, mock data, and generators. Does not affect validation constraints |
enum | Allowed values (mutually exclusive with dict and value) |
dict | Dictionary reference (mutually exclusive with enum and value) |
pii | PII metadata (reserved keys: kind, masker; extra keys allowed) |
x-opentp | Tooling extensions (custom checks and role hints) |
Constraints (JSON-Schema-like, portable):
- string:
minLength,maxLength,pattern,format(hint) - number/integer:
minimum,maximum,exclusiveMinimum,exclusiveMaximum,multipleOf - array:
items(scalar-only),minItems,maxItems,uniqueItems
Examples:
Constant value
event_name: value: login_click x-opentp: role: constantString with constraints
email: type: string example: user@example.com format: email maxLength: 320Payload slot with logical name
Use name when the payload field key is a transport or vendor slot, but the field has a clearer logical/code-facing name:
dimension_1: type: string name: orgType title: Organization Type description: Logical organization type stored in analytics slot dimension_1. enum: [startup, enterprise, agency] example: enterprisedimension_1 remains the canonical payload field key. name does not rename the payload field or change validation paths.
Array of scalar items
tags: type: array example: [auth, login] uniqueItems: true items: type: string minLength: 1PII metadata
Section titled “PII metadata”Fields can optionally include a pii object with masking and governance metadata.
Two keys are reserved and understood by tooling:
pii.kind— what kind of PII this is (e.g.email,ip,user_id)pii.masker— masker implementation id
Additional pii.* keys are allowed for governance and can be validated by tooling using spec.events.pii.schema from opentp.yaml.
The specification defines a built-in masker id star that replaces the value with asterisks.
Other masker ids are tooling-defined.
event.aliases
Section titled “event.aliases”Track previous event keys for migrations:
aliases: - key: old_login_click deprecated: reason: Renamed for consistency date: "2025-01-01"event.ignore
Section titled “event.ignore”Skip specific validations:
ignore: - path: payload::legacy_field reason: Temporary field for migrationCommon path conventions (tooling-defined):
event.key(orkey) — skip key validation checkstaxonomy.<field>— skip validation for a taxonomy fieldpayload::\<field>— skip validation for a payload field across all selectors/targetspayload.<target>.schema.<field>— skip validation for a field on a specific targetpayload.<target>.<version>.schema.<field>— skip validation for a field on a specific target+version