One conversation, one object, one documented contract.

Every processed conversation returns as a vCon conforming to draft-ietf-vcon-vcon-core. The intelligence rides on it as a declared, non-critical extension carried in typed analysis[] objects, each one independently versioned. A system that has never heard of Tresic still parses the conversation and ignores what it does not recognise.

Read this first

Five things that will otherwise cost you a day. None of them are edge cases; all four of the first are the normal path.

There is no transcript in the object

The enriched vCon is an overlay. dialog[] is optional in vcon-core and we do not populate it, so do not write code that assumes a transcript is present. The transcript stays in its authoritative home and is fetched separately by source_call_id, which is preserved verbatim from the input and is your join key back to the source conversation.

An absent signal is not a negative signal

Which signals appear in conversation-signals is declared per industry. A signal that is absent from the signals object was never extracted for that industry. That is a different fact from fired: false, which means it was extracted and did not fire. Key on presence before you read the boolean.

Identity is carried as IDs, never as names

Associations to a customer, an employee, a competitor, or a partner are stamped as stable identifiers. Current names and details are resolved at read time through the Customer Data API. This is deliberate: a name change never rewrites a conversation record, and bulk corrections are made by ID.

Association IDs can legitimately be absent

Reference data is optional per tenant. mapping_status reports mapped, partial, or unmapped, and when an association cannot be resolved the ID is absent rather than guessed. The raw party addresses and source_call_id are always preserved so you can attribute downstream yourself. Aggregate accordingly.

Do not key on the vcon version parameter

It is deprecated in vcon-core and is present only for conformance. Version discovery is per object: read analysis[].type together with its schema tag. Nothing about our release cadence is expressed in that top-level value.

The envelope

Standard vcon-core members, plus the two fields that make the object joinable and retainable.

FieldTypeNotes
vconstringCore version parameter, deprecated. Always "0.4.0". Never key on it.
uuidstringThe vCon's own identifier. Stable across re-delivery of the same conversation.
created_atRFC 3339Always present.
updated_atRFC 3339 or nullNull until the object is amended.
extensions[]string[]Lists tresic-enrichment whenever intelligence is attached.
critical[]string[]tresic-enrichment is deliberately not listed, so a consumer that does not understand it may still process the vCon safely.
parties[]object[]Required vcon-core member, always emitted. Per-party role, name, and channel address (tel for voice, SMS and video; mailto for email and chat). This is where raw caller identifiers survive on an unmapped conversation. Join by index to conversation-attribution for resolved IDs.
source_call_idstringPreserved verbatim from the input. The join key back to the source conversation and to its transcript.
pipeline_metadataobjectProvenance only: vertical, the version of the analysis logic for that industry, and data_classification, which drives retention. Deliberately slim.
analysis[]object[]The intelligence, one typed object per output. See below.

Operational telemetry is not on the wire. Timings, token counts, cost, and processing-strategy internals are ours to run on, not yours to parse.

The typed analysis objects

Each enrichment output is its own vendor-attributed object with its own versioned schema tag. There is no bundle to unpack and no flat mirror of these fields at the top level: read by type.

analysis[] object envelope
{
  "type":     "conversation-signals",
  "vendor":   "tresic",
  "product":  "vcon-enrichment",
  "schema":   "tresic/conversation-signals@1.0",
  "encoding": "json",
  "body":     { /* the type's payload */ }
}

Match on type and vendor: "tresic". A vCon may legitimately carry analysis objects from other vendors alongside ours.

TypeSchema tagAlways presentWhat the body carries
conversation-classification tresic/conversation-classification@1.0 Yes What the conversation is: primary_topic and primary_topic_id with primary_confidence, secondary_topics[], topic_complexity, conversation_type with party counts, disposition, and the physical channel facts (direction, originator, destination).
conversation-signals tresic/conversation-signals@1.0 Yes A normalised risk and opportunity vocabulary. Each signal is an independent {fired, details}, where details is the one-sentence evidence when it fired and null when it did not. Every signal and its definition →
conversation-resolution tresic/conversation-resolution@1.0 Yes resolution is one of resolved, follow_up_required, or no_issue, with resolution_details. Definitions below.
summary standard vcon-core type Yes title, plain_text_summary, and action_items[]. Factual recap only: who, why, what, and the outcome.
sentiment standard vcon-core type Yes overall_sentiment (positive, neutral, negative) and sentiment_direction (higher, steady, lower), which is the trajectory across the conversation rather than a second reading of it.
conversation-attribution schema not published Yes Who the conversation was with, as IDs: customer_id, per-party employee_id, matched competitor_ids and partner_ids, and mapping_status. Carried so you can read it; its schema is tied to our resolver and identifier scheme and is not published.
conversation-captured-data tresic/conversation-captured-data@1.0 No Structured data captured during the conversation, as captures[] of {capture_type, fields}. Emitted only when there was something to capture, so the object's presence is itself the signal that the conversation captured data. Most conversations do not carry it.

Two definitions you cannot use the fields without

Resolution. resolved means handled to completion on this conversation. follow_up_required means genuinely worked but still pending something - a ticket, a dispatch, a handoff, a callback, an awaited answer. no_issue means there was nothing substantive to resolve. First-contact resolution is therefore resolved / (resolved + follow_up_required), and no_issue is deliberately outside that denominator.

Churn. The churn signal fires on dissatisfaction plus a stated intent to leave. Frustration on its own is a complaint, not churn. This distinction is the field's definition rather than a tuning preference, and a consumer building a retention worklist needs it to read the field correctly.

A complete object

Emitted by the shipping contract code, values synthesised. One inbound support conversation where two signals fired and the outcome is still open.

enriched vCon · complete payload
{
  "vcon": "0.4.0",
  "uuid": "36e3298c-7e6f-4045-90bd-b5f693b66c75",
  "vcon_id": "36e3298c-7e6f-4045-90bd-b5f693b66c75",
  "created_at": "2026-07-24T14:12:07Z",
  "updated_at": null,
  "extensions": [
    "tresic-enrichment"
  ],
  "critical": [],
  "parties": [
    {
      "role": "employee",
      "name": "A. Whitfield",
      "tel": "204"
    },
    {
      "role": "customer",
      "name": "R. Okafor",
      "tel": "+15005550142"
    }
  ],
  "source_call_id": "8f2c41d0-6a11-4e7b-9c33-2b5e7a441c08",
  "pipeline_metadata": {
    "vertical": "csps-msps",
    "version": "v1.4",
    "data_classification": "CLASS_PII"
  },
  "analysis": [
    {
      "type": "conversation-classification",
      "vendor": "tresic",
      "product": "vcon-enrichment",
      "schema": "tresic/conversation-classification@1.0",
      "encoding": "json",
      "body": {
        "primary_topic": "Technical Support - Infrastructure & Connectivity",
        "primary_topic_id": "T001",
        "primary_confidence": 0.93,
        "secondary_topics": [
          {
            "topic": "Account Management & Service Review",
            "topic_id": "T005",
            "confidence": 0.41
          }
        ],
        "topic_complexity": "moderate",
        "conversation_type": {
          "type": "employee_customer",
          "employee_count": 1,
          "external_count": 1
        },
        "direction": "inbound",
        "originator": "+15005550142",
        "destination": "204",
        "disposition": {
          "disposition": "standard",
          "confidence": 0.98,
          "reason": "Substantive two-party business conversation."
        }
      },
      "dialog": null,
      "mediatype": null
    },
    {
      "type": "conversation-signals",
      "vendor": "tresic",
      "product": "vcon-enrichment",
      "schema": "tresic/conversation-signals@1.0",
      "encoding": "json",
      "body": {
        "signals": {
          "churn": {
            "fired": true,
            "details": "Customer stated they are being quoted by another provider before renewal."
          },
          "complaint": {
            "fired": true,
            "details": "Second fault report in a week; frustration at a repeat visit."
          },
          "expansion": {
            "fired": false,
            "details": null
          },
          "product_request": {
            "fired": false,
            "details": null
          },
          "buy_signal": {
            "fired": false,
            "details": null
          }
        }
      },
      "dialog": null,
      "mediatype": null
    },
    {
      "type": "conversation-resolution",
      "vendor": "tresic",
      "product": "vcon-enrichment",
      "schema": "tresic/conversation-resolution@1.0",
      "encoding": "json",
      "body": {
        "resolution": "follow_up_required",
        "resolution_details": "Field dispatch scheduled; customer awaiting confirmation."
      },
      "dialog": null,
      "mediatype": null
    },
    {
      "type": "summary",
      "vendor": "tresic",
      "product": "vcon-enrichment",
      "schema": "tresic-enrichment/summary@1.0",
      "encoding": "json",
      "body": {
        "title": "Repeat voice quality fault, second report in a week",
        "plain_text_summary": "Customer called about dropped calls at their second location, the second report in a week. Engineer confirmed packet loss on the site circuit and raised a field dispatch for 25 July. Customer said they are being quoted by another provider and want the fault resolved before their contract date.",
        "action_items": [
          {
            "action_id": "411a647b-d1b2-43a5-894c-2ccfc5b3a1a2",
            "task_type": null,
            "description": "Confirm the field dispatch window with the customer",
            "owner_role": null,
            "owner_name": null,
            "priority": "high",
            "deadline_suggested": null,
            "context": null,
            "source_evidence": null,
            "crm_action": null,
            "follow_up_required": false,
            "follow_up_type": null
          },
          {
            "action_id": "5b4b34a1-5b67-48ef-9d86-8f780b638dba",
            "task_type": null,
            "description": "Review circuit history for the second location before dispatch",
            "owner_role": null,
            "owner_name": null,
            "priority": "medium",
            "deadline_suggested": null,
            "context": null,
            "source_evidence": null,
            "crm_action": null,
            "follow_up_required": false,
            "follow_up_type": null
          }
        ]
      },
      "dialog": null,
      "mediatype": null
    },
    {
      "type": "sentiment",
      "vendor": "tresic",
      "product": "vcon-enrichment",
      "schema": "tresic-enrichment/sentiment@1.0",
      "encoding": "json",
      "body": {
        "overall_sentiment": "negative",
        "sentiment_direction": "higher"
      },
      "dialog": null,
      "mediatype": null
    },
    {
      "type": "conversation-attribution",
      "vendor": "tresic",
      "product": "vcon-enrichment",
      "schema": "tresic/conversation-attribution@1.0",
      "encoding": "json",
      "body": {
        "customer_id": {
          "external_id": "cus_7Q2M4X",
          "identity_provider": null,
          "system_of_record": "tresic",
          "urn": "tresic:master:cid/cus_7Q2M4X"
        },
        "customer_match_confidence": "address_name_agree",
        "customer_id_alternative": null,
        "caller_organization": "Northgate Dental Group",
        "competitor_ids": [
          {
            "external_id": "meridian_voice",
            "identity_provider": null,
            "system_of_record": "internal",
            "urn": "tresic:sor/internal:cid/meridian_voice"
          }
        ],
        "partner_ids": [],
        "mapping_status": "mapped",
        "participants": [
          {
            "role": "employee",
            "employee_id": {
              "external_id": "emp_3K9F1B",
              "identity_provider": "tresic",
              "system_of_record": null,
              "urn": "tresic:master:uid/emp_3K9F1B"
            },
            "employee_match_confidence": "extension_name_agree",
            "employee_id_alternative": null
          },
          {
            "role": "customer",
            "employee_id": null,
            "employee_match_confidence": null,
            "employee_id_alternative": null
          }
        ]
      },
      "dialog": null,
      "mediatype": null
    }
  ]
}

Note what this object does not contain: no transcript, no model or processing internals, no resolved customer or employee name, and no signal that this industry does not declare. Those absences are the contract, not gaps in the example.

Versioning, and what we promise not to do

Each analysis type carries its own semantic version in its schema tag, so types move independently and a change to one never forces a change to your handling of another.

Additive changes are minor

New fields arrive as a minor version bump. Ignore what you do not recognise and something added next quarter cannot break what you shipped this quarter.

Names are never reused

A field or enum name is never renamed in place and never repurposed to mean something else. If the meaning changes, the name changes and the version changes with it.

Breaking changes ship beside, not over

A breaking change is a new major version published as its own schema. The version you integrated against does not shift underneath you.

New signals arrive the same way: as additional entries inside conversation-signals, on the object you already consume. Bringing another industry to production adds classification your existing integration inherits without asking for it.

Why this is a standard and not our format

The vCon container is on the IETF standards track: draft-ietf-vcon-vcon-core defines the JSON format, and our object conforms to it rather than wrapping it.

The intelligence travels with the record

Because the enrichment is a declared extension on a standard container rather than rows in our database, the conversation and everything derived from it move together, through any tooling that speaks vCon. If you stop working with us, what you hold is still a standard object that still parses.

The types are the contribution

There is no standard answer yet to "was this resolved" or "what happened that someone should act on." We publish and version those types - classification, signals, resolution, captured data - as the citable contract, and every conversation we process is a reference implementation of them. Tresic contributes to the vCon work at the IETF and to the CPaaS Acceleration Alliance AI and Data working group.

Hold one of these before you commit to anything.

API access comes with credentials against a sandbox tenant and a corpus to build against, so you can read real objects rather than reason about a specification.

Get API access