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

# Tracing OpenAI Realtime API with WebSockets

export const plans_0 = "Any"

export const deployments_0 = "Any"

export const data_plane_version_0 = undefined

export const use_case_0 = undefined

<Note>
  **Applies to:**

  * Plan - {plans_0}
  * Deployment - {deployments_0}
  * {data_plane_version_0}
  * {use_case_0}
</Note>

Summary

Configure tracing and observability for OpenAI Realtime API WebSocket sessions using Braintrust's OpenTelemetry integration. This approach allows you to log session-based traces, capture event metadata, and maintain full control over event schemas without relying on the Braintrust proxy.

## Configuration Steps

### Step 1: Configure OpenTelemetry for Braintrust

Set up OpenTelemetry to send traces directly to Braintrust's OTEL endpoint with proper authentication and project context.

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.braintrust.dev/otel
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer , x-bt-parent=project_name:"

```

### Step 2: Initialize session-based tracing

Create a root span for the entire WebSocket session to track the long-lived connection and session-level metadata.

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
// Java example using OpenTelemetry
Span sessionSpan = tracer.spanBuilder("voice_interaction")
    .setAttribute("braintrust.input", userSpeechText)
    .setAttribute("braintrust.metadata", JSON.toJson(Map.of(
        "user_id", "user-123",
        "session_id", "session-456",
        "audio_duration_ms", 2500
    )))
    .setAttribute("braintrust.tags", JSON.toJson(List.of("voice", "realtime-api")))
    .startSpan();

```

### Step 3: Log individual events as nested spans

Create child spans for specific WebSocket events like RAG retrieval and LLM generation to maintain granular observability.

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
// Nested span for RAG retrieval
Span retrievalSpan = tracer.spanBuilder("rag_retrieval")
    .setParent(Context.current().with(sessionSpan))
    .setAttribute("braintrust.metadata", JSON.toJson(Map.of(
        "documents_considered", List.of(
            Map.of("id", "kb_123", "title", "Product FAQ", "relevance", 0.92),
            Map.of("id", "kb_456", "title", "User Guide", "relevance", 0.78)
        ),
        "retrieval_method", "semantic_search"
    )))
    .startSpan();

```

### Step 4: Capture conversation data in root span

Store complete conversation context and metadata in the root span to support custom scorers and analysis tools.

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
// Store conversation data for scoring
sessionSpan.setAttribute("braintrust.output", llmResponse);
sessionSpan.setAttribute("braintrust.metadata", JSON.toJson(Map.of(
    "conversation_history", conversationMessages,
    "rag_documents", retrievedDocuments,
    "total_tokens", tokenCount,
    "session_duration_ms", sessionDuration
)));

```

### Step 5: Close spans and flush data

Properly end spans when events complete and ensure data is sent to Braintrust.

```text theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
// End nested spans first
retrievalSpan.end();

// End session span when WebSocket closes
sessionSpan.setAttribute("braintrust.metrics", JSON.toJson(Map.of(
    "total_messages", messageCount,
    "total_duration_ms", sessionDuration
)));
sessionSpan.end();

// Flush telemetry data
OpenTelemetry.getGlobalOpenTelemetry().shutdown();

```

## Helpful Links

* [OpenTelemetry integration documentation](/integrations/sdk-integrations/opentelemetry)
* [OpenTelemetry GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/)
