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

# Properties

> Pass arbitrary custom dimensions through to span attributes.

`properties` attaches arbitrary key/value pairs to a span. Values pass through as-is, with no namespace prefix, so they stay readable by other OTel backends after a switch.

## Usage

```python theme={null}
import bentolabs_sdk.analytics as bento

bento.track_ai(
    event="search",
    properties={
        "feature": "semantic_search",
        "experiment": "v3",
        "ranking_model": "ce-3",
    },
    user_id="u1",
)
```

`properties` is also accepted by:

* `bento.begin(properties=...)`
* `interaction.update(properties=...)`
* `interaction.finish(properties=...)`
* `bento.tool_span(name, properties=...)`
* `interaction.tool_span(name, properties=...)`

## Type fidelity

Property values keep their type so downstream filters and aggregates work correctly:

| Value                                            | Span sees              | Why it matters                      |
| ------------------------------------------------ | ---------------------- | ----------------------------------- |
| `"foo"` (str)                                    | string                 |                                     |
| `42` (int)                                       | int                    | dashboard can `experiment_id > 100` |
| `3.14` (float)                                   | float                  |                                     |
| `True` (bool)                                    | bool                   | dashboard can `is_premium = true`   |
| `[1, 2, 3]` (homogeneous list)                   | array                  |                                     |
| `{"nested": "x"}` or `[1, "two"]` (dict / mixed) | JSON string (fallback) | OTel doesn't natively support these |

<Warning>
  Don't put `gen_ai.*`, `input.value`, or `output.value` keys in `properties`. The SDK-managed kwargs are written *after* properties and will overwrite them. Use the dedicated kwargs (`user_id`, `model`, `provider`, `input`, `output`, `convo_id`) instead.
</Warning>

## Properties vs built-in kwargs

User, conversation, model, provider, input, and output have dedicated kwargs, because the dashboard has filters and breakdowns built for them. Everything else goes in `properties`: feature flags, experiment buckets, latency, retrieved-chunk count. Each one becomes a generic filter and breakdown dimension.

A property that proves widely useful can get promoted to a first-class column. Until then, anything in `properties` lands in `spans.attributes` as JSONB, queryable from custom dashboards.
