Skip to main content
Everything you send to Moda is stored as messages grouped into conversations, optionally attached to a user and an environment. This page defines each object and the fields that control how data is grouped and attributed. For exact wire formats, see the Ingestion API reference.

Conversation

A conversation is an ordered sequence of messages exchanged between an end user (or another system) and your agent, identified by a conversation_id string. It is the unit nearly every Moda analysis operates on: summaries, world state, signals, use cases, and problems are all computed per conversation. Conversations appear in the dashboard at Observe → Conversations and through the Data API’s conversation endpoints.

Setting the conversation ID

Any non-empty string works as a conversation ID. Use an identifier your application already has — a session ID, thread ID, run ID, or ticket ID — and set it before the first model call. Set it in one of three ways:
  • SDKsmoda.conversation_id = "..." in Python, Moda.withConversationId(...) in Node.js.
  • Ingestion API — the required conversation_id field on every /v1/ingest event.
  • OpenTelemetry — the moda.conversation_id span attribute on /v1/traces (see OpenTelemetry).

Message

A message is one event inside a conversation: a user turn, an agent response, a system prompt, or a tool interaction. The SDKs create messages automatically from instrumented LLM calls. The field names below are as they appear on POST /v1/ingest events; each has an OTLP-attribute equivalent on /v1/traces.

User

A user is an end user of your agent, identified by an optional user_id string — an opaque identifier from your own system. Set it with moda.user_id = "..." (Python), Moda.withUserId(...) (Node.js), the user_id event field on /v1/ingest, or the moda.user_id OTLP attribute. Setting user_id powers:
  • The Users page (Understand → Users): per-user health and activity, cohorts such as power users and at-risk users, and a per-user detail view with inferred traits, intent distribution, frustration trajectory, tool failures, and an activity feed.
  • User filtering: the conversations list in the dashboard and the user_id parameter on GET /conversations.
  • User-impact metrics, such as the Users Impacted KPI on tool failures.
Conversations ingested without a user_id are not attributed to any individual user: they are excluded from per-user rows and cohorts, and the Users page rolls them into a single aggregated anonymous row instead.

Environment

Every message belongs to one of three environments: development, staging, or production. Environments keep test traffic out of production analytics: the Conversations page has an environment filter, and the Data API accepts an environment parameter on GET /conversations. Set the environment with:
  • the environment field on the /v1/ingest request body, with an optional per-event override
  • the environment option of Moda.init(...) in Node.js
  • the moda.environment span attribute or deployment.environment resource attribute over OTLP; in Python, pass resource_attributes={"deployment.environment": "staging"} to moda.init(...)
Common aliases are normalized: dev and develop become development; stg and stage become staging; prod becomes production. Any unrecognized or missing value is recorded as production.

Prompt attribution fields

Messages can carry prompt_id, prompt_name, prompt_version, and prompt_version_id to link a model call to a managed prompt version. The same link is available as moda.prompt_key / moda.prompt_version (and related) OTLP span attributes, and as telemetry metadata in the Vercel AI SDK integration. Version IDs come from the .moda/prompts.lock.json file written by moda prompts sync. These fields power per-version usage and comparison in Prompts. See Prompt attribution for the full mechanism.

Next steps