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

# Metadata

> Attach custom key-value pairs to traces for filtering, debugging, and context

Metadata lets you attach custom key-value pairs to your AgentMark traces. Use metadata to add context like user IDs, environment names, feature flags, request IDs, and customer tiers, then filter and search by those values in the Dashboard.

<Note>
  Developers configure metadata in your application. See [Tracing setup](/observe/tracing-setup) for setup instructions.
</Note>

## Setting metadata

There are two ways to attach metadata to traces: via `experimental_telemetry.metadata` on the model call, and via the `span()` function's `metadata` option when grouping traces.

### Via telemetry metadata

Pass metadata in `experimental_telemetry` on the model call. AgentMark attaches these key-value pairs to the resulting generation span:

```typescript theme={null}
import { AgentMarkSDK } from "@agentmark-ai/sdk";
import { createAgentMark } from "@agentmark-ai/prompt-core";
import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";

const sdk = new AgentMarkSDK({
  apiKey: process.env.AGENTMARK_API_KEY,
  appId: process.env.AGENTMARK_APP_ID,
});

sdk.initTracing({ registerGlobally: true });

// The neutral client only loads + renders prompts; your code calls the model.
const client = createAgentMark({ loader: sdk.getApiLoader() });

const prompt = await client.loadTextPrompt("greeting.prompt.mdx");
const { messages, text_config } = await prompt.format({
  props: { query: "Hello" },
});

const result = await generateText({
  model: openai(text_config.model_name.replace(/^openai\//, "")),
  messages,
  experimental_telemetry: {
    isEnabled: true,
    functionId: "my-function",
    metadata: {
      user_id: "user-123",
      environment: "production",
      feature: "chat-v2",
      customerTier: "enterprise",
    },
  },
});
```

### Via the span function

Pass metadata when creating a trace group. The metadata lives on the trace's root span and powers trace-level filtering:

```typescript theme={null}
import { span } from "@agentmark-ai/sdk";

const { result, traceId } = await span(
  {
    name: "my-workflow",
    metadata: {
      requestId: "req-abc-123",
      version: "2.1.0",
      environment: "production",
    },
  },
  async (ctx) => {
    const prompt = await client.loadTextPrompt("handler.prompt.mdx");
    const { messages, text_config } = await prompt.format({
      props: { query: "What is AgentMark?" },
    });

    return await generateText({
      model: openai(text_config.model_name.replace(/^openai\//, "")),
      messages,
      experimental_telemetry: { isEnabled: true },
    });
  }
);
```

`SpanOptions.metadata` has the type `Record<string, string>`, so values must be strings. Convert numbers, booleans, and other types before passing.

<Tip>
  You can combine both approaches. Metadata on `span()` applies to the parent trace, while `experimental_telemetry.metadata` applies to the individual generation spans within that trace.
</Tip>

## Filtering by metadata

In the AgentMark Dashboard, metadata keys are auto-discovered from your trace data. You can filter traces by any metadata key that appears in your data.

To filter by metadata:

1. Navigate to the **Traces** tab in the Dashboard.
2. Open the filter dropdown.
3. Look for entries prefixed with **Metadata:** followed by the key name (for example, "Metadata: environment").
4. Select the key you want to filter by.
5. Choose an operator and enter a value.

AgentMark supports the following filter operators for metadata values:

* **equals** / **notEquals**: exact match or no match on the value
* **contains** / **notContains**: value includes or excludes the specified substring
* **starts with**: value begins with the specified string
* **ends with**: value ends with the specified string
* **exists**: the key is present, regardless of value
* **`does not exist`**: the key isn't present on the trace

You can combine and save filters as views. See [Filtering and search](/observe/filtering-and-search).

## Metadata in the trace detail

When viewing an individual trace in the Dashboard, metadata appears in the attributes section. The Dashboard displays all key-value pairs you attached, making it easy to see the full context of a trace without switching to your application logs.

## How AgentMark stores metadata

AgentMark stores metadata as string key-value pairs. It indexes keys for fast filtering and search, and it stores all values as strings. If you need to attach non-string values, convert them first:

```typescript theme={null}
metadata: {
  userId: "user-123",
  resultCount: String(results.length),   // number → string
  isRetry: String(isRetry),              // boolean → string
}
```

## Best practices

### Recommended metadata keys

* **`user_id`**: Per-user debugging and cost attribution. Example: `"user-123"`. Use snake\_case to populate the trace's user field (AgentMark stores camelCase `userId` as ordinary metadata).
* **`session_id`**: Group related traces into a session. Must be snake\_case in metadata, or use the top-level `span()` `sessionId` option. Example: `"sess-abc"`
* **`environment`**: Distinguish staging from production when using a single app. Example: `"production"`
* **`version`**: Track which application version generated the trace. Example: `"2.1.0"`
* **`requestId`**: Correlate AgentMark traces with your application logs. Example: `"req-xyz"`
* **`feature`**: Identify which feature or flow triggered the trace. Example: `"chat-v2"`

### Tips

* **Use consistent key names** across your application. If one service sends `userId` and another sends `user_id`, they appear as separate keys in the Dashboard.
* **Keep values short.** Metadata suits identifiers and labels, not large payloads.
* **Use metadata for anything you want to filter by later.** If you find yourself searching your application logs for a value, it's a good candidate for metadata.
* **Metadata keys are case-sensitive.** AgentMark treats `userId` and `userid` as different keys.
* **AgentMark stores all values as strings.** Convert numbers, booleans, and other types before passing. `SpanOptions.metadata` has the type `Record<string, string>`; AgentMark converts values in `experimental_telemetry.metadata` to strings on ingest.

### Reserved keys

AgentMark promotes some keys to dedicated trace fields on ingest instead of storing them as custom metadata: `session_id`, `session_name`, `user_id`, `trace_name`, `prompt_name`, `props`, and the `dataset_*` keys (`dataset_run_id`, `dataset_run_name`, `dataset_path`, `dataset_item_name`, `dataset_expected_output`, `dataset_input`). A custom key with one of these names doesn't appear under the **Metadata:** filter fields, so pick a different name for unrelated values (for example `app_props` instead of `props`).

## Next steps

<CardGroup cols={2}>
  <Card title="Traces and logs" icon="chart-line" href="/observe/traces-and-logs">
    Understand trace details and span attributes
  </Card>

  <Card title="Sessions" icon="users" href="/observe/sessions">
    Group related traces together
  </Card>

  <Card title="Filtering and search" icon="filter" href="/observe/filtering-and-search">
    Filter traces by metadata keys
  </Card>

  <Card title="Tracing setup" icon="code" href="/observe/tracing-setup">
    Set up tracing in your application
  </Card>
</CardGroup>

<div className="mt-8 rounded-lg bg-blue-50 p-6 dark:bg-blue-900/30">
  <h3 className="font-semibold mb-3">Have questions?</h3>
  <p className="mb-4">Reach out any time:</p>

  <ul>
    <li>
      Email the team at <a href="mailto:hello@agentmark.co" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200">[hello@agentmark.co](mailto:hello@agentmark.co)</a> for support
    </li>

    <li>
      Schedule an <a href="https://cal.com/ryan-randall/enterprise" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200">Enterprise Demo</a> to learn about AgentMark's business solutions
    </li>
  </ul>
</div>
