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

# Bring traces from any source

> Send OpenTelemetry traces from Braintrust, Galileo, Arize/Phoenix, LangSmith, or any OTLP exporter to Surface Area, and link them to sessions and worlds.

Surface Area ingests standard OpenTelemetry traces. Point any OTLP exporter at your project's endpoint and the platform normalizes the vendor's attribute conventions into traces, observations, sessions, and worlds. One endpoint, one authentication scheme, nothing provider-specific on the wire.

## The endpoint

| | |
| - | - |
| URL | `POST {GATEWAY_HOST}/api/public/otel/v1/traces` |
| Auth | HTTP Basic: `pk-lf-...` as the username, `sk-lf-...` as the password |
| Body | OTLP `ExportTraceServiceRequest` as protobuf (`application/x-protobuf`) or JSON (`application/json`), optionally gzip-encoded |

Configure any OpenTelemetry SDK exporter with two environment variables:

```bash theme={null}
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="$GATEWAY_HOST/api/public/otel/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Basic $(printf '%s:%s' "$GATEWAY_PUBLIC_KEY" "$GATEWAY_SECRET_KEY" | base64)"
```

## What is recognized

The ingestion layer reads the conventions below with no configuration. A span carrying attributes from more than one vocabulary is fine; the `gateway.*` namespace wins when two disagree.

| Source | Conventions read |
| - | - |
| Braintrust | `braintrust.input`, `braintrust.input_json`, `braintrust.output`, `braintrust.output_json`, `braintrust.metadata` (JSON or flattened `braintrust.metadata.<key>`), `braintrust.span_attributes.type` / `.name`, `braintrust.tags`, `braintrust.metrics.prompt_tokens` / `.completion_tokens` / `.tokens`, `braintrust.metadata.model` |
| Arize, Phoenix, Galileo, Agno, and other OpenInference exporters | `openinference.span.kind`, `session.id`, `user.id`, `input.value`, `output.value`, `llm.input_messages.*`, `llm.output_messages.*`, `llm.model_name`, `llm.token_count.*` |
| OpenTelemetry GenAI semantic conventions | `gen_ai.operation.name`, `gen_ai.request.model`, `gen_ai.response.model`, `gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.usage.*`, `gen_ai.conversation.id` |
| LangSmith, Langfuse, Vercel AI SDK, Pydantic AI, Logfire, MLflow, TraceLoop, Google ADK, LiveKit, CrewAI, AgentOps | Their native attribute names, as documented in the REST API reference |

Braintrust `span_attributes.type` values map to observation types: `llm` becomes a generation, `tool` a tool call, `task` and `function` spans, `eval` and `score` evaluator observations.

### Where a trace came from

Every trace and observation records `gateway.otel.source` in its metadata. The value is detected from the instrumentation scope name and attribute namespaces (`braintrust`, `openinference`, `otel-genai`, `langsmith`, and so on). Set `gateway.otel.source` yourself as a span or resource attribute to override detection with any label, for example `galileo`. Filter on that metadata key to compare providers side by side.

## Sessions

A span's session id is read from `session.id`, `gen_ai.conversation.id`, `langfuse.session.id`, or `gateway.session.id`, on the span first and then on the resource. Exporters that keep the session elsewhere (Braintrust stores it in `braintrust.metadata`) can add a `session.id` span attribute, or the whole export can be forced into one session with `gateway traces import --session`. Traces sharing a session id are grouped into one session automatically.

## Link imported traces to a world

World sessions run on the platform write flat metadata that the UI, evaluators, and exports understand. Imported traces join the same views when they carry the same keys, either on each span or once on the OTLP resource:

| Key | Meaning |
| - | - |
| `gateway.world` | World slug |
| `gateway.world.version` | World version label |
| `gateway.task` | Task name |
| `gateway.session.kind` | Kind of session, for example `live-world-session` |

Resource-level values are hoisted onto every trace and observation, so an exporter that cannot set per-span attributes can still link a whole run by tagging its resource.

## Import an export file

When the source cannot stream to you but can export OTLP/JSON (an OpenTelemetry collector `file` exporter, a saved API response, a fixture), push the file with the CLI:

```bash theme={null}
gateway traces import ./collector-output.jsonl \
  --source galileo \
  --world support-desk --world-version 1.4.0 --task triage
```

`traces import` accepts one request document (`{"resourceSpans": [...]}`), a bare `resourceSpans` array, a JSON array of requests, or JSONL with one request per line, and reads stdin with `-`. The `--source`, `--world`, `--world-version`, `--task`, `--session`, `--environment`, and `--attr key=value` flags add resource attributes; a span's own attributes always take precedence. Use `--dry-run` to parse and count without sending.

<Info>
  The command posts to the same `/api/public/otel/v1/traces` endpoint your SDKs use, with the same `GATEWAY_HOST` / `GATEWAY_PUBLIC_KEY` / `GATEWAY_SECRET_KEY` credentials or the saved `gateway auth login`.
</Info>

## Example: Braintrust

Braintrust's OpenTelemetry exporter emits `braintrust.*` attributes. Point it at Surface Area:

```bash theme={null}
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="$GATEWAY_HOST/api/public/otel/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Basic $(printf '%s:%s' "$GATEWAY_PUBLIC_KEY" "$GATEWAY_SECRET_KEY" | base64)"
```

A span such as

```json theme={null}
{
  "name": "chat",
  "attributes": [
    {"key": "braintrust.span_attributes.type", "value": {"stringValue": "llm"}},
    {"key": "braintrust.input_json", "value": {"stringValue": "[{\"role\":\"user\",\"content\":\"hi\"}]"}},
    {"key": "braintrust.output_json", "value": {"stringValue": "{\"role\":\"assistant\",\"content\":\"hello\"}"}},
    {"key": "braintrust.metadata", "value": {"stringValue": "{\"model\":\"gpt-4o\",\"session_id\":\"s-1\"}"}},
    {"key": "braintrust.metrics.prompt_tokens", "value": {"intValue": 12}},
    {"key": "braintrust.metrics.completion_tokens", "value": {"intValue": 7}}
  ]
}
```

lands as a generation named `chat` with model `gpt-4o`, input and output preserved, usage 12/7, the metadata object kept, and `gateway.otel.source: braintrust`.

## Example: Arize, Phoenix, or Galileo through OpenInference

OpenInference instrumentors (`openinference-instrumentation-*`) attach `openinference.span.kind`, `input.value`, `output.value`, `llm.*`, `session.id`, and `user.id`. Configure their tracer provider with an OTLP exporter aimed at the endpoint above; no Surface Area SDK is required. Galileo's OpenTelemetry integration uses the same conventions plus the OpenTelemetry GenAI attributes, both of which are read.

## Related

* [Export to Surface Area](./export) for the SDK's own exporter configuration.
* [Sessions & traces](./sessions) for how sessions group traces.
* [Metadata & identity](./metadata) for `gateway.*` attributes you can set from any exporter.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.