# Connect traces

Turn real requests from your app into test cases. Cases from production show what your users really send, which generated cases can't. OptBench reads traces three ways: an OpenTelemetry push, a PostHog pull and a Langfuse pull.

## Data rules first

Before any trace is read, set what OptBench and your agent may use: dashboard, **Data → Rules**. Until you save them, only files are on; PostHog, Langfuse and OpenTelemetry are off.

- **Sources your agent may read:** PostHog, Langfuse, OpenTelemetry, files.
- **Limits:** how many days back, and how many cases per import.
- **Never import records that contain:** your list of words. A case whose text contains one is dropped, in the preview too.
- **What an imported case keeps:** the recorded answer, and the recorded tool results that let agent cases replay their tools.

Rules are set in the dashboard only; an agent can read them (`biy_data_rules_get`) but not change them. Every import checks them before anything is read: a source that is off is refused, and redaction is always on. Emails, phone numbers, IBANs, card numbers, IP addresses, API keys and tokens become placeholders such as `<EMAIL_1>` before anything is stored or sent to a model.

## OpenTelemetry push

Your app, or your Collector, sends a copy of its GenAI spans to OptBench. Your own backend keeps everything.

- **Ingest key.** An Admin creates it in **Data → Sources → OpenTelemetry**. It is shown once and starts with `optbench_ingest_`. It only works for sending spans: the MCP server and the API refuse it. Keep it in an environment variable (`OPTBENCH_INGEST_KEY`), never in a committed file.
- **A second exporter.** In a Collector, add an `otlphttp/optbench` exporter with the header `authorization: "Bearer ${env:OPTBENCH_INGEST_KEY}"` and list it next to your existing exporter in the traces pipeline. Without a Collector, set the SDK's OTLP traces endpoint and header. The OpenTelemetry page shows both snippets with your endpoint.
- **Attribute length.** Some exporters or span processors cap attribute length and cut `gen_ai.input.messages`, `gen_ai.output.messages` or `gen_ai.tool.call.result` in the middle. That breaks the JSON. Shorten the text inside each message instead, so the value stays valid JSON with every message in it, and allow a higher limit (64k characters) on the traces you send to OptBench.
- **What OptBench keeps.** Only GenAI spans, at the sample rate you set (an Admin can lower it). Traces wait in staging for 24 hours, at most 500 of them, until you import them into a use case. The page shows whether your app is tracking: how recently spans arrived, and how many carry a prompt and an answer.

Your agent can set this up in your code. In the dashboard, the OpenTelemetry page has a prompt for it.

## Start from the traces that already reach OptBench

When spans are pushed to OptBench, or a PostHog or Langfuse source is connected directly, your agent can start from the traces themselves. It needs no export of its own.

- `biy_traces_list` shows what waits, grouped by agent or span name. Each group comes with the use case it most likely belongs to and why: at least half of the tools the traces called must be the use case's tools, else a word of the agent's name. You see names and counts only, never content. "Unclear" means several use cases fit, for example agents that share their tools; then you choose.
- **OpenTelemetry:** `biy_traces_import(use_case_id, name=…, pick=50)` previews about 50 traces spread over what production asks. It takes every tool set and agent first, then spreads across clusters of similar requests, with complete runs first and no near-duplicates. It runs locally, with no model call. You import them on the OpenTelemetry page.
- **A direct source:** `biy_data_sources_pull(data_source_id, use_case_id, span_name=…)` pulls one feature at a time with the stored key: preview first, then `preview=false`, `consent=true` and the preview's `upload_id` after you say yes. One pull reads at most 10 MB. A big agent trace that alone doesn't fit is left out with a warning.
- `biy_lifecycle_get(use_case_id)` says where a use case stands (before launch, live, or both) and what to do next.

## PostHog and Langfuse pulls

Two ways, and you can use either.

- **With your agent (recommended).** Your agent exports traces on your machine with your own PostHog or Langfuse key. The key never reaches OptBench. The agent reads your data rules, lists what exists (span or event names, counts, field names; no content) and reports it with `biy_data_findings_report`, then asks you which traces to use for which use case. It registers the source (`biy_data_sources_register`), uploads the export (`biy_upload_url`) and imports it with `biy_cases_import`.
- **Direct.** Connect the source in the dashboard: **Connect with PostHog** (you approve on PostHog's own screen), or a PostHog or Langfuse key. The key is stored encrypted and never shown again. Then choose **Pull** on the source and pick the use case.

`biy_data_sources_list` shows every source and how many cases came from each.

## Preview before import

Every import shows a preview first and stores nothing: the cases it would add, what was redacted, and its warnings. Cases are added only when you confirm. Your agent shows you the preview and waits for your yes (`biy_cases_import` with `preview=true`, then `preview=false` and `consent=true`).

## Refresh sample

When a use case already has cases from a source, **Pull** becomes **Refresh sample**. It adds only the requests the use case has no case for yet, and says what it skipped:

```text
12 new · 38 already there · 140 repeats skipped
```

"Already there" means the use case has a case with the same input, whatever gold you wrote on it since. "Repeats" are duplicates within this import.

## Another agent's traces

A trace source often holds more than one agent. For an agent use case, a trace whose recorded tool calls are mostly not this use case's tools belongs to another agent. OptBench skips it, counts it in the preview, and suggests the use case in your workspace that has most of those tools. Import those traces there instead.

## After the import

- Imported cases carry today's production answer, and that is never gold. Next: [Write gold](/docs/workflow-gold).
- To delete imported data: **Data → Imports & storage** deletes one import, and **Delete imported cases** on a source deletes every case that came from it.
- What OptBench does with your data: [Your data](/your-data).
