> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moda.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Data model

> The objects Moda stores — conversations, messages, users, environments — and the fields that control grouping and prompt attribution.

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](/ingestion/http-api).

## 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](/data-api/conversations).

### 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:

* **SDKs** — `moda.conversation_id = "..."` in [Python](/ingestion/python), `Moda.withConversationId(...)` in [Node.js](/ingestion/node).
* **Ingestion API** — the required `conversation_id` field on every `/v1/ingest` event.
* **OpenTelemetry** — the `moda.conversation_id` span attribute on `/v1/traces` (see [OpenTelemetry](/ingestion/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`](/ingestion/http-api) events; each has an OTLP-attribute equivalent on `/v1/traces`.

| Field                                                                                             | Description                                                                                                                                                                                                                                                                               |
| ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `role` (required)                                                                                 | Typically `user`, `assistant`, or `system`. `assistant` messages are recorded as agent output; every other role is recorded as input to the agent.                                                                                                                                        |
| `content` / `message` / `content_blocks`                                                          | The message body: a plain string, or structured content blocks of type `text`, `thinking`, `tool_use`, `tool_result`, or `image`. The conversation detail Full Trace view renders these blocks and can filter the timeline by prompts, agent responses, thinking, tool calls, and images. |
| `timestamp`                                                                                       | ISO 8601. Defaults to the time Moda received the event.                                                                                                                                                                                                                                   |
| `input_tokens`, `output_tokens`, `reasoning_tokens`, `cache_read_tokens`, `cache_creation_tokens` | Token usage for the model call, including provider cache buckets.                                                                                                                                                                                                                         |
| `model`, `provider`                                                                               | Which model produced the response and which provider served it — for example `gpt-4o` on `openai`.                                                                                                                                                                                        |
| `finish_reason`                                                                                   | Why the model stopped: `stop`, `length`, `tool_calls`, and so on.                                                                                                                                                                                                                         |
| `agent_name`                                                                                      | In multi-agent systems, which agent authored the message. The dashboard [Home page](/dashboard/overview) has an agent selector that scopes its metrics by this field; the OTLP equivalent is `gen_ai.agent.name`.                                                                         |
| `external_message_id`                                                                             | Your own stable, unique ID for the message.                                                                                                                                                                                                                                               |
| `user_id`                                                                                         | The end user the message belongs to — see [User](#user).                                                                                                                                                                                                                                  |
| `environment`                                                                                     | Per-message override of the request-level environment — see [Environment](#environment).                                                                                                                                                                                                  |
| `prompt_id`, `prompt_name`, `prompt_version`, `prompt_version_id`                                 | Link the message to a managed prompt version — see [Prompt attribution fields](#prompt-attribution-fields).                                                                                                                                                                               |

## 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](/dashboard/users-and-tools)): 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`](/data-api/conversations).
* **User-impact metrics**, such as the Users Impacted KPI on [tool failures](/dashboard/signals).

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`](/data-api/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](/prompt-management/overview). See [Prompt attribution](/prompt-management/attribution) for the full mechanism.

## Next steps

* [Analysis](/concepts/analysis) — what Moda computes from this data and where each result appears.
* [Ingestion overview](/ingestion/overview) — pick an ingestion path for your stack.
* [Ingestion API reference](/ingestion/http-api) — exact request schemas, limits, and error envelopes.
* [Prompt attribution](/prompt-management/attribution) — wire prompt versions into your telemetry.
