Customer Interaction

CustomerInteraction v1.0

Schema Type: customer-interactionVersion: v1.0JSON Schema: https://schemas.platform.ontopix.ai/customer-interaction/v1.0.jsonViews: https://schemas.platform.ontopix.ai/customer-interaction/v1.0.views.json

Overview

CustomerInteraction provides a unified, event-based format for capturing customer interactions across multiple channels and modalities (audio, text, video, hybrid), with optional enrichment via metrics and semantic analysis.

The schema is designed for three primary uses:

  1. Storage and interchange — a canonical representation of any customer service interaction regardless of channel or source system
  2. LLM enrichment — a target for automated quality analysis, sentiment detection, topic extraction, and summarization
  3. Audit evaluation — the primary input for audit-criteria policies, which evaluate interactions against structured quality indicators

Required fields

{
  "schema_type": "customer-interaction",
  "schema_version": "v1.0",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source": {
    "media": "text",
    "channel": "whatsapp"
  },
  "time": {
    "start": "2025-01-15T10:00:00Z"
  },
  "participants": [
    { "id": "p1", "role": "customer" }
  ],
  "events": [
    {
      "id": "e1",
      "type": "message",
      "time_offset": { "start": 0.0 },
      "participant_id": "p1",
      "content": { "text": "Hello, I need help." }
    }
  ]
}

Root-level field reference

FieldTypeRequiredDescription
schema_typeconst "customer-interaction"YesSchema type identifier
schema_versionstringYesVersion. Pattern: ^v\d+\.\d+(-(alpha|beta|rc)\d+)?$
idstring (minLength 1)YesUnique interaction identifier (UUID recommended)
sourceobjectYesOrigin and technical context. See source.
timeobjectYesTemporal boundaries. See time.
participantsarray (min 1)YesParticipants in the interaction. See participants[].
eventsarray (min 1)YesChronological event stream. See events[].
languagesarrayNoDetected languages with probability scores. See languages[].
summaryobjectNoHigh-level interaction summary. See summary.
metricsobjectNoInteraction-level quantitative metrics. See Metrics.
analysisobjectNoInteraction-level qualitative analysis. See Analysis.
metadataobjectNoFree-form key-value pairs (JsonValue values).

source (required)

Origin and technical context of the interaction.

FieldTypeRequiredDescription
source.mediaenumYesPrimary modality: audio, text, video, hybrid
source.channelstring (minLength 1)YesSpecific channel, e.g. whatsapp, email, webchat, phone, sms
source.synchronicityenumNoInteraction cadence: synchronous (live call/video), asynchronous (email/SMS), near-synchronous (chat/messaging)
source.source_systemstringNoUpstream system or vendor (e.g. genesys, twilio, zendesk, salesforce)
source.referencestring (uri)NoExternal reference in the source system. URI-like format recommended (e.g. zendesk://ticket/12345, genesys://conversation/abc-def)
source.raw_contentstring (uri)NoURI to the original raw content (e.g. s3://bucket/recordings/call.wav)
source.references_basestring (uri)NoURI prefix for resolving events[].references[].file paths. Example: s3://bucket/pkg-id/sources/. See events[].references[].

time (required)

Temporal boundaries of the interaction. Uses absolute ISO 8601 timestamps.

FieldTypeRequiredDescription
time.startstring (date-time)YesISO 8601 timestamp when the interaction started
time.endstring (date-time)NoISO 8601 timestamp when the interaction ended
time.durationnumber (≥ 0)NoDuration in seconds (may be derived from start/end or independently measured)

participants[] (required, min 1)

Each participant represents an actor in the interaction. Two orthogonal fields capture participant identity:

  • role (required): which side of the interaction the participant represents (business semantics)
  • actor_type (optional): how the participant operates (technical implementation)

This separation enables consistent analytics across mixed human/bot interactions — e.g., filtering all customer_support participants regardless of whether they are human agents, IVRs, or AI bots.

role values

ValueMeaning
customerThe service consumer
customer_supportThe service provider (agents, bots, IVRs)
systemAutomated infrastructure (routing, notifications)
unknownRole not yet determined; to be inferred during enrichment

actor_type values

ValueMeaning
humanA person
ivrInteractive Voice Response — DTMF-based voice menu
interactive_menuText-based menu with structured options (buttons, quick replies)
ai_agentConversational AI with natural language understanding
automatedAutomated process without interaction capability (auto-responders, notifications)
ambientAuditory presence without a producing agent — hold music, ringtones, IVR tones, silence, environmental noise. See Ambient participants.

If actor_type is omitted, the implementation type is unknown or irrelevant.

Ambient participants

actor_type: "ambient" represents non-speech audio captured in a recording (hold music, ringtones, IVR tones, silence, background noise) — material that exists in the audio stream but is not produced by an agent participating in the conversation.

Channel attribution stays in role. Ambient audio is attributed to the side of the recording channel where it was captured, not collapsed into a synthetic neutral side:

  • Hold music or IVR tones captured on the agent's channel → role: "customer_support"
  • Background noise on the customer's side → role: "customer"
  • Mono recordings without channel separation → role: "unknown"

Events. Events referencing an ambient participant typically use type: "call_segment" (the segment is still part of the call; it just contains no speech). content.text carries a free-text bracketed label such as "[hold music]", "[ringtone]", "[silence]".

Consumer note. When an event's participant has actor_type: "ambient", its call_segment does not contain transcribed speech. Consumers performing speech analysis (sentiment, word counts, intent detection) should filter out events whose participant is ambient. For example: identify hold time as the cumulative duration of call_segment events whose participant has actor_type: "ambient" and role: "customer_support".

Example:

{
  "participants": [
    { "id": "p1", "role": "customer", "actor_type": "human" },
    { "id": "p2", "role": "customer_support", "actor_type": "human" },
    { "id": "ambient-cs", "role": "customer_support", "actor_type": "ambient" }
  ],
  "events": [
    {
      "id": "e1",
      "type": "call_segment",
      "time_offset": { "start": 60.0, "end": 180.0 },
      "participant_id": "ambient-cs",
      "content": { "text": "[hold music]" }
    }
  ]
}

Participant fields

FieldTypeRequiredDescription
participants[].idstring (minLength 1)YesUnique participant identifier (referenced by events[].participant_id)
participants[].roleenumYesSee role values above
participants[].actor_typeenumNoSee actor_type values above
participants[].namestringNoDisplay name
participants[].identifiersobjectNoMap of external identifiers (e.g. email, phone, crm_id). Values are strings.
participants[].metricsobjectNoParticipant-level metrics (same shape as root metrics)
participants[].analysisobjectNoParticipant-level analysis signals (same shape as root analysis)
participants[].metadataobjectNoAdditional metadata. Values constrained to JsonValue.

events[] (required, min 1)

events is the canonical timeline of the interaction. Each event belongs to exactly one participant and has a relative temporal position (time_offset) measured in seconds from interaction start.

Temporal model distinction:

  • Root time uses absolute ISO 8601 timestamps for the overall interaction boundaries
  • Event time_offset uses relative seconds from interaction start for temporal positioning within the interaction

Event fields

FieldTypeRequiredDescription
events[].idstring (minLength 1)YesUnique event identifier
events[].typeenumYesmessage, call_segment, system_notification, status_change
events[].time_offsetobjectYesRelative temporal position. See time_offset.
events[].participant_idstring (minLength 1)YesReference to participants[].id
events[].contentobjectYesEvent payload. See content.
events[].metricsobjectNoEvent-level metrics (same shape as root metrics)
events[].analysisobjectNoEvent-level analysis signals (same shape as root analysis)
events[].languagestringNoISO 639-1 language code for this specific event (e.g. "en", "es")
events[].referencesarrayNoSource files used to derive this event. See events[].references[].

Event types

ValueUse case
messageText-based communication (chat, email, SMS)
call_segmentSpeech-based communication (phone, video call) — may include word-level timing
system_notificationAutomated messages (queue updates, transfer notices, auto-replies)
status_changeState transitions (queued → active → resolved)

events[].time_offset

FieldTypeRequiredDescription
time_offset.startnumber (≥ 0)YesStart offset in seconds from interaction start
time_offset.endnumber (≥ 0)NoEnd offset in seconds from interaction start
time_offset.durationnumber (≥ 0)NoDuration in seconds

events[].content

FieldTypeRequiredDescription
content.subjectstring (minLength 1)NoSubject line of the event (e.g. email subject). Only meaningful for channels that support subjects.
content.textstringYesMessage text or speech transcript
content.text_formatenumNoplain (default), html, markdown, rich_text
content.audio_urlstring (uri)NoURL to audio recording
content.attachments[]arrayNoAttached files or media
content.attachments[].urlstring (uri)YesAttachment URL
content.attachments[].typestringYesMIME type (e.g. image/png, application/pdf)
content.attachments[].namestringNoOriginal filename
content.metadataobjectNoEvent-level metadata (JsonValue values)
content.words[]arrayNoWord-level timing alignment (for transcripts). See below.

events[].content.words[]

Word-level timing data from speech transcription.

FieldTypeRequiredDescription
words[].textstring (minLength 1)YesWord text
words[].start_offsetnumber (≥ 0)YesStart time in seconds from event start
words[].end_offsetnumber (≥ 0)YesEnd time in seconds from event start
words[].confidencenumber 0, 1NoTranscription confidence score

events[].references[]

Per-event lineage: source files that were used to generate or derive this event. Distinct from events[].content.attachments[], which models files attached to the message itself (e.g. a PDF the customer sent in an email). Both arrays can coexist on the same event.

Paths in references[].file are resolved against source.references_base when present; otherwise they are opaque strings whose meaning is defined by the consumer (for example, the ELSA ZIP ingest pipeline treats them as relative to the package's sources/ directory).

FieldTypeRequiredDescription
references[].filestring (minLength 1)YesPath to the source file. Resolved against source.references_base when present.
references[].typeenumYesType of source: email, image, document, chat, audio, other.
references[].rationalestring (minLength 1)NoFree-text explanation of how this file was used.

Example:

{
  "id": "evt-001",
  "type": "message",
  "time_offset": { "start": 0 },
  "participant_id": "agent-1",
  "content": { "text": "Estimado cliente, en respuesta a su consulta..." },
  "references": [
    { "file": "emails/01.eml", "type": "email", "rationale": "Body paragraph 2" },
    { "file": "notes/policy.md", "type": "document" }
  ]
}

languages[] (optional)

Detected languages with confidence scores for the entire interaction. Multiple entries represent code-switching or multi-lingual participants.

FieldTypeRequiredDescription
languages[].languagestringYesISO 639-1 language code. Pattern: ^[a-z]{2}$ (e.g. en, es, fr)
languages[].probabilitynumber 0, 1YesConfidence score for language detection

For per-event language annotation, use events[].language.


summary (optional)

High-level interaction summary distilling the entire conversation into key takeaways.

FieldTypeDescription
summary.titlestringHuman-readable title or subject (e.g. "Billing dispute - incorrect charge")
summary.overviewstring|null2–5 sentence narrative: what the customer wanted, what was done, what the outcome was
summary.resolution_statusenum|nullresolved, unresolved, partial, escalated, pending, or null
summary.key_topics[]arraystring|nullMain topics discussed (e.g. ["billing", "refund"])
summary.interaction_flow[]array|nullOrdered functional milestones referencing specific events. See below.
summary.customer_satisfactionobject|nullSatisfaction measurement. See below.

summary.interaction_flow[]

Each entry represents a functional milestone in the interaction, linked to a specific event.

FieldTypeRequiredDescription
interaction_flow[].event_idstringYesReference to events[].id. Any non-empty string (e.g. UUID or event_1).
interaction_flow[].activityenumYesrequest, acknowledgment, information, action, resolution, escalation, handoff, follow_up, closure
interaction_flow[].descriptionstringNoHuman-readable description of this milestone

summary.customer_satisfaction

FieldTypeRequiredDescription
customer_satisfaction.scoreinteger 0, 10YesSatisfaction score (0 = completely dissatisfied, 10 = completely satisfied)
customer_satisfaction.collection_methodenumNosurvey (direct customer input), inferred (AI-estimated), agent_reported

metadata (optional)

Free-form key-value pairs for session-level metadata. Values are constrained to JsonValue (any valid JSON type: string, number, boolean, null, object, array).

"metadata": {
  "campaign_id": "PROMO-2024",
  "customer_tier": "premium",
  "priority_level": 3
}

Metrics (optional, transversal)

metrics appears at three levels with the same structure:

  • root (metrics): aggregated over the entire interaction
  • participants[].metrics: aggregated per participant
  • events[].metrics: for the individual event

metrics is a flat map where:

  • keys use dot-notation (e.g. count.words, rate.words_per_minute)
  • values are objects: { "value": <number>, "unit": <string>, "context"?: <string> }

Custom metric keys must use the x. prefix (e.g. x.acme.my_metric).

Standard metrics

KeyUnitValue typeApplicable atDescription
count.wordswordsinteger ≥ 0root, participant, eventTotal words
count.characterscharactersinteger ≥ 0root, participant, eventTotal characters
count.syllablessyllablesinteger ≥ 0root, participant, eventTotal syllables
count.messagesmessagesinteger ≥ 0root, participantTotal messages
count.eventseventsinteger ≥ 0root, participantTotal events
count.participantsparticipantsinteger ≥ 0root onlyTotal participants
count.interruptionsinterruptionsinteger ≥ 0root, participantInterruption count
time.response_secondssecondsnumber ≥ 0event onlyTime to respond
rate.words_per_minutewpmnumber ≥ 0root, participant, eventSpeaking/typing rate
rate.syllables_per_minutespmnumber ≥ 0root, participant, eventSyllable rate
ratio.talk_timerationumber 0, 1root, participantProportion of time active
avg.by_message.wordswords/messagenumber ≥ 0root, participantAverage words per message
avg.by_message.syllablessyllables/messagenumber ≥ 0root, participantAverage syllables per message
avg.by_message.response_secondsseconds/messagenumber ≥ 0root, participantAverage response time
distribution.expressive_state.sentiment.<value>rationumber 0, 1root, participantDistribution over positive, neutral, negative, null
distribution.expressive_state.emotion.<value>rationumber 0, 1root, participantDistribution over 13 emotions (see Analysis signals)
distribution.expressive_state.urgency.<value>rationumber 0, 1root, participantDistribution over low, medium, high, null
distribution.expressive_state.clarity.<value>rationumber 0, 1root, participantDistribution over clear, unclear, null
distribution.communicative_intent.<label>rationumber 0, 1root, participantDistribution over intent labels
distribution.semantic_content.topics.<topic>rationumber 0, 1root, participantDistribution over topic labels
distribution.pragmatic_function.message_type.<value>rationumber 0, 1root, participantDistribution over message types (see Analysis signals)

Analysis (optional, transversal)

analysis appears at three levels with the same structure:

  • root (analysis): over the entire interaction
  • participants[].analysis: per participant
  • events[].analysis: per event

analysis is a flat map where:

  • keys use dot-notation (e.g. expressive_state.sentiment, semantic_content.topics)
  • values are objects: { "value": <JsonValue>, "confidence"?: <0..1>, "context"?: <string> }

Custom analysis keys must use the x. prefix.

Standard analysis signals

KeyValue typeApplicable atDescription
expressive_state.sentimentenum|null: positive, neutral, negativeroot, participant, eventOverall sentiment polarity
expressive_state.sentiment_scorenumber -1, 1root, participant, eventContinuous sentiment score
expressive_state.emotionenum|nullevent onlyDetected emotion: anger, anxiety, confusion, contempt, disgust, fear, frustration, gratitude, joy, neutral, relief, sadness, surprise
expressive_state.tonestring|nullevent onlyCommunicative tone (free-form)
expressive_state.formality_levelenum|null: formal, informal, neutral, mixedevent onlyFormality level
expressive_state.urgencyenum|null: low, medium, highevent onlyPerceived urgency
expressive_state.clarityenum|null: clear, unclearevent onlyMessage clarity
communicative_intentstring (minLength 1)root, participant, eventDetected communicative intent
pragmatic_function.message_typeenumevent onlygreeting, question, command, acknowledgment, response, statement, interjection, closing
pragmatic_function.speech_actstring (pattern ^[a-z][a-z0-9_]*(\.[a-z0-9_]+)*$)event onlySpeech act (controlled taxonomy)
semantic_content.topicsarraystringroot, participant, eventTopics/themes detected
semantic_content.key_pointsarraystringroot, participant, eventKey points

Available Views

ViewDescription
one-linerSingle-line identification: id, participant count, event count
summaryContext-setting fields: id, channel, media, participants (id/role/name), event count, overview
fullComplete object (identity projection)
for-enrichmentCompact projection for LLM enrichment: id, channel, participants (id/role/name), events (id, participant_id, text)
for-auditProjection optimized for quality auditing: id, channel, media, participants (id/role), events (id, type, participant_id, text, offset)

Examples

Minimal valid document

{
  "schema_type": "customer-interaction",
  "schema_version": "v1.0",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source": {
    "media": "text",
    "channel": "whatsapp"
  },
  "time": {
    "start": "2025-12-29T10:00:00Z"
  },
  "participants": [
    { "id": "p1", "role": "customer" },
    { "id": "p2", "role": "customer_support" }
  ],
  "events": [
    {
      "id": "e1",
      "type": "message",
      "time_offset": { "start": 0.0 },
      "participant_id": "p1",
      "content": { "text": "Hola, tengo un problema con mi pedido." }
    },
    {
      "id": "e2",
      "type": "message",
      "time_offset": { "start": 20.0 },
      "participant_id": "p2",
      "content": { "text": "Gracias, ¿me confirmas el número de pedido?" }
    }
  ]
}

Complete document with enrichment

{
  "schema_type": "customer-interaction",
  "schema_version": "v1.0",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source": {
    "media": "text",
    "channel": "whatsapp",
    "synchronicity": "near-synchronous",
    "source_system": "zendesk",
    "reference": "zendesk://ticket/12345",
    "references_base": "s3://elsa-ingest/pkg-abc123/sources/"
  },
  "time": {
    "start": "2025-12-29T10:00:00Z",
    "end": "2025-12-29T10:02:00Z",
    "duration": 120
  },
  "participants": [
    {
      "id": "p1",
      "role": "customer",
      "actor_type": "human",
      "name": "Alex",
      "metrics": { "count.messages": { "value": 1, "unit": "messages" } },
      "analysis": {
        "expressive_state.sentiment": { "value": "neutral", "confidence": 0.72 }
      }
    },
    {
      "id": "p2",
      "role": "customer_support",
      "actor_type": "human",
      "name": "Sam",
      "identifiers": { "employee_id": "AGT-1234" }
    }
  ],
  "events": [
    {
      "id": "e1",
      "type": "message",
      "time_offset": { "start": 0.0 },
      "participant_id": "p1",
      "content": {
        "text": "Hola, tengo un problema con mi pedido.",
        "metadata": { "order_id": "ORD-123" }
      },
      "metrics": {
        "count.words": { "value": 8, "unit": "words" },
        "time.response_seconds": { "value": 0, "unit": "seconds" }
      },
      "analysis": {
        "expressive_state.sentiment": { "value": "neutral", "confidence": 0.72 },
        "expressive_state.urgency": { "value": "medium", "confidence": 0.65 },
        "pragmatic_function.message_type": { "value": "request" }
      },
      "language": "es",
      "references": [
        { "file": "chats/whatsapp-thread.json", "type": "chat", "rationale": "Original customer message" }
      ]
    },
    {
      "id": "e2",
      "type": "message",
      "time_offset": { "start": 20.0 },
      "participant_id": "p2",
      "content": { "text": "Gracias, ¿me confirmas el número de pedido?" },
      "metrics": {
        "count.words": { "value": 9, "unit": "words" },
        "time.response_seconds": { "value": 20, "unit": "seconds" }
      },
      "analysis": {
        "pragmatic_function.message_type": { "value": "question" }
      }
    }
  ],
  "languages": [{ "language": "es", "probability": 0.99 }],
  "summary": {
    "title": "Order issue inquiry",
    "overview": "Customer asked for help with an order; agent requested order number.",
    "resolution_status": "pending",
    "key_topics": ["order", "support"],
    "interaction_flow": [
      { "event_id": "event_1", "activity": "request" },
      { "event_id": "event_2", "activity": "acknowledgment" }
    ],
    "customer_satisfaction": {
      "score": 8,
      "collection_method": "survey"
    }
  },
  "metrics": {
    "count.messages": { "value": 2, "unit": "messages" },
    "count.words": { "value": 17, "unit": "words" },
    "distribution.expressive_state.sentiment.neutral": { "value": 1.0, "unit": "ratio" },
    "distribution.semantic_content.topics.order": { "value": 0.6, "unit": "ratio" },
    "distribution.semantic_content.topics.support": { "value": 0.4, "unit": "ratio" }
  },
  "analysis": {
    "semantic_content.topics": { "value": ["order", "support"] },
    "expressive_state.sentiment": { "value": "neutral", "confidence": 0.72 }
  }
}

Multi-participant with IVR, AI, and human agent

{
  "schema_type": "customer-interaction",
  "schema_version": "v1.0",
  "id": "interaction-complex-001",
  "source": { "media": "hybrid", "channel": "phone", "source_system": "genesys" },
  "time": {
    "start": "2025-12-30T09:00:00Z",
    "end": "2025-12-30T09:10:00Z",
    "duration": 600
  },
  "participants": [
    { "id": "p1", "role": "customer",          "actor_type": "human",        "name": "Maria García",       "identifiers": { "phone": "+34600123456" } },
    { "id": "p2", "role": "customer_support",  "actor_type": "ivr",          "name": "Main IVR Menu" },
    { "id": "p3", "role": "customer_support",  "actor_type": "ai_agent",     "name": "Virtual Assistant" },
    { "id": "p4", "role": "customer_support",  "actor_type": "human",        "name": "Agent Carlos",       "identifiers": { "employee_id": "AGT-1234" } },
    { "id": "p5", "role": "system",                                           "name": "Call Router" }
  ],
  "events": [
    { "id": "e1", "type": "system_notification", "time_offset": { "start": 0.0  }, "participant_id": "p5", "content": { "text": "Call initiated and routed to IVR" } },
    { "id": "e2", "type": "message",             "time_offset": { "start": 5.0  }, "participant_id": "p2", "content": { "text": "Welcome. Press 1 for billing, 2 for technical support." } },
    { "id": "e3", "type": "message",             "time_offset": { "start": 15.0 }, "participant_id": "p1", "content": { "text": "2" } },
    { "id": "e4", "type": "system_notification", "time_offset": { "start": 20.0 }, "participant_id": "p5", "content": { "text": "Transferred to AI assistant" } },
    { "id": "e5", "type": "message",             "time_offset": { "start": 25.0 }, "participant_id": "p3", "content": { "text": "Hello! What technical issue are you experiencing?" } },
    { "id": "e6", "type": "message",             "time_offset": { "start": 35.0 }, "participant_id": "p1", "content": { "text": "My internet keeps dropping every few minutes." } },
    { "id": "e7", "type": "message",             "time_offset": { "start": 45.0 }, "participant_id": "p3", "content": { "text": "Let me connect you with a specialist." } },
    { "id": "e8", "type": "system_notification", "time_offset": { "start": 50.0 }, "participant_id": "p5", "content": { "text": "Escalated to human agent" } },
    { "id": "e9", "type": "message",             "time_offset": { "start": 60.0 }, "participant_id": "p4", "content": { "text": "Hi Maria, I'm Carlos. Let me check your service status." } }
  ]
}

This example shows:

  • customer + human: the caller
  • customer_support + ivr: initial voice routing
  • customer_support + ai_agent: conversational triage bot
  • customer_support + human: specialist agent
  • system: infrastructure router

Analytics can filter by role ("all customer_support"), actor_type ("compare human vs ai_agent"), or both.


Migration from v1.0-beta1

No breaking changes. All v1.0-beta1 data is valid v1.0 data.

New field in v1.0: events[].language (optional, ISO 639-1 string). Previously, per-event language could only be stored in events[].analysis as a custom field. The new events[].language provides a canonical location for this annotation.

Additive updates within v1.0

The v1.0 schema has received additive enum extensions since first publication. All previously valid documents remain valid; consumers that strictly validate enum membership should accept the new values gracefully.

  • participants[].actor_type: added "ambient" for non-speech audio captured in recordings (hold music, ringtones, IVR tones, silence, background noise). See Ambient participants.
  • expressive_state.formality_level: added "neutral" (in addition to formal, informal, mixed).
  • summary.interaction_flow[].event_id: relaxed to allow any non-empty string (UUID or any application-defined identifier).
  • source.references_base and events[].references[]: added optional per-event source-file lineage. events[].references[] is an array of { file, type, rationale? } objects recording which source materials were used to derive each event; source.references_base is an optional URI prefix used to resolve relative file paths. Distinct from events[].content.attachments[] (which models files attached to the message itself). See events[].references[].