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