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

# Ingestão por OpenTelemetry

> Envie os traces gen_ai.* que o seu agente já emite para o endpoint OTLP da Niadra.

Se o seu agente já emite traces de OpenTelemetry com os atributos `gen_ai.*`, ele pode escrever na Niadra sem chamada nova no caminho da conversa: aponte um exportador OTLP para a Niadra, e cada chamada ao modelo vira as mensagens da conversa, com quem falou e quando. É o jeito mais rápido de começar a construir memória a partir de um agente que você não quer mexer, e um caminho comum para capturar uma plataforma de fornecedor que exporta traces mas não tem integração com a Niadra.

A leitura continua por `context()`, pelas ferramentas do histórico ou pelo MCP. O OpenTelemetry cobre o lado da escrita.

## O endpoint

A Niadra recebe OTLP por HTTP com corpo em JSON (`Content-Type: application/json`):

```text theme={null}
POST https://acme-prod.us-east-1.api.niadra.com/v1/otel/v1/traces
```

Autentique com a chave da fonte em `Authorization: Bearer`, a mesma que o seu agente usaria no SDK, com o escopo `track`. A resposta é a do OTLP: spans recusados contam em `partialSuccess.rejectedSpans`, com os motivos em `errorMessage`. Corpo em protobuf é recusado com 422; configure o exportador em `http/json`.

## O que a Niadra lê de um span

As convenções semânticas `gen_ai` ainda mudam, e várias gerações de atributos convivem nas bibliotecas em uso hoje. A Niadra lê em cascata, então você não precisa travar a versão de biblioteca por nossa causa:

| O que a Niadra precisa | Onde procura, em ordem                                                                                                                                                                                                                                                                                                  |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| As mensagens           | `gen_ai.input.messages` e `gen_ai.output.messages` (atual); depois `gen_ai.prompt` e `gen_ai.completion` em JSON; depois os indexados `gen_ai.prompt.{n}.role`/`content` e `gen_ai.completion.{n}.role`/`content`; depois os do OpenInference, `llm.input_messages.{n}.message.*` e `llm.output_messages.{n}.message.*` |
| Quem falou             | O papel da mensagem: `user` e `human` viram o cliente, `assistant` e `model` o agente de IA. Mensagens de sistema, de desenvolvedor e de ferramenta ficam fora da conversa                                                                                                                                              |
| A conversa             | `gen_ai.conversation.id`, depois `session.id`, depois o id do trace                                                                                                                                                                                                                                                     |
| Quando                 | O início do span para as mensagens de entrada, o fim para as de saída                                                                                                                                                                                                                                                   |

Duas coisas não têm atributo no OpenTelemetry, então você mesmo marca, no span ou no recurso:

| Atributo               | Exemplo                                 | Por quê                                                                                                              |
| ---------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `niadra.channel`       | `whatsapp`                              | Não existe atributo de OpenTelemetry para o canal do cliente. Sem ele, vale o padrão da fonte                        |
| `niadra.handle.<tipo>` | `niadra.handle.app_user_id = "u-48213"` | O cliente de quem a conversa fala, um atributo por tipo de handle. `enduser.id` também serve e vira um `app_user_id` |

Cada span repete a conversa até ali, então cada mensagem ganha a chave de idempotência da posição dela na conversa: um histórico exportado de novo é deduplicado, em vez de duplicado.

<Warning>
  Atributos de span podem parar em todo destino para onde o seu coletor exporta. Prefira um id interno (`app_user_id` ou `enduser.id`) a um telefone ou e-mail, e mande os atributos da Niadra só no pipeline que vai para a Niadra. Dado pessoal nunca viaja na URL.
</Warning>

## Passos

### 1. Aponte um exportador para a Niadra

Acrescente um exportador OTLP ao lado do que você já tem, com a codificação em JSON.

O SDK de OpenTelemetry para Node.js tem exportador em JSON. O SDK de Python só exporta em protobuf, então mande os spans dele para um OpenTelemetry Collector e deixe o Collector repassar em JSON para a Niadra.

<CodeGroup>
  ```yaml Collector theme={null}
  exporters:
    otlphttp/niadra:
      traces_endpoint: https://acme-prod.us-east-1.api.niadra.com/v1/otel/v1/traces
      encoding: json
      headers:
        Authorization: "Bearer ${env:NIADRA_API_KEY}"

  service:
    pipelines:
      traces/niadra:
        receivers: [otlp]
        exporters: [otlphttp/niadra]
  ```

  ```python Python theme={null}
  # The agent exports to the Collector as usual; the Collector forwards JSON to Niadra.
  from opentelemetry import trace
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
  from opentelemetry.sdk.trace import TracerProvider
  from opentelemetry.sdk.trace.export import BatchSpanProcessor

  provider = TracerProvider()
  provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")))
  trace.set_tracer_provider(provider)
  ```

  ```typescript TypeScript theme={null}
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
  import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; // JSON over HTTP

  const niadraExporter = new OTLPTraceExporter({
    url: "https://acme-prod.us-east-1.api.niadra.com/v1/otel/v1/traces",
    headers: { Authorization: `Bearer ${process.env.NIADRA_API_KEY}` },
  });

  const provider = new NodeTracerProvider({
    spanProcessors: [new BatchSpanProcessor(niadraExporter)],
  });
  provider.register();
  ```

  ```sh Shell theme={null}
  # Zero-code instrumentation in Node.js, straight to Niadra
  export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://acme-prod.us-east-1.api.niadra.com/v1/otel/v1/traces"
  export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer ${NIADRA_API_KEY}"
  export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="http/json"
  ```
</CodeGroup>

### 2. Marque o canal e o cliente

Marque os atributos da Niadra no span que leva as mensagens, ou uma vez no recurso, para um processo que atende um canal só. Mantenha `gen_ai.conversation.id` estável durante a conversa inteira: ele vira o `conversation_id`.

<CodeGroup>
  ```python Python theme={null}
  tracer = trace.get_tracer("support-agent")

  with tracer.start_as_current_span("chat wa-8812") as span:
      span.set_attribute("gen_ai.conversation.id", "wa-8812")
      span.set_attribute("niadra.channel", "whatsapp")
      span.set_attribute("niadra.handle.app_user_id", "u-48213")
      reply = client.chat.completions.create(model=MODEL, messages=messages)
  ```

  ```typescript TypeScript theme={null}
  import { trace } from "@opentelemetry/api";

  const tracer = trace.getTracer("support-agent");

  await tracer.startActiveSpan("chat wa-8812", async (span) => {
    span.setAttributes({
      "gen_ai.conversation.id": "wa-8812",
      "niadra.channel": "whatsapp",
      "niadra.handle.app_user_id": "u-48213",
    });
    const reply = await client.chat.completions.create({ model: MODEL, messages });
    span.end();
    return reply;
  });
  ```
</CodeGroup>

Os atributos são lidos do próprio span e do recurso dele. Quando a instrumentação do cliente do modelo grava as mensagens num span filho, marque os atributos da Niadra no recurso, ou copie para aquele span com um processador de spans.

### 3. Ligue a captura de conteúdo

A maioria das instrumentações `gen_ai` deixa o conteúdo das mensagens de fora por padrão. A Niadra precisa dele para construir memória, então ligue a captura de conteúdo na instrumentação que você usa (em muitas instrumentações de Python, `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`). Se preferir manter o conteúdo fora dos seus outros destinos, rode um segundo pipeline só para a Niadra.

### 4. Confira se os eventos chegaram

Um span vira mensagens com as mesmas garantias de um lote: o conteúdo bruto é gravado antes da resposta e os eventos são ordenados pelo momento em que aconteceram. Leia a linha do tempo do cliente para conferir:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://acme-prod.us-east-1.api.niadra.com/v1/history/timeline" \
    -H "Authorization: Bearer $NIADRA_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "subject": { "type": "app_user_id", "value": "u-48213" }, "limit": 5 }'
  ```
</CodeGroup>

## Quando usar o SDK

O OpenTelemetry registra o que o modelo viu e disse. O SDK acrescenta o que os traces não levam: o contexto lido antes da resposta e o `context_stamp` de cada turno do agente, a verificação com `verify()`, a ligação entre handles com `identify()`, as ações de agente com `closes`, as transferências e o fim da conversa. É isso que alimenta a medição do [aproveitamento do contexto](/concepts/context-use). Um caminho comum é começar pelo OpenTelemetry para construir memória desde o primeiro dia e depois acrescentar `context()` e as escritas do SDK nos agentes que atendem o cliente.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Eventos e o lote" href="/concepts/events">
    o que é um evento e como ele é deduplicado.
  </Card>

  <Card title="Início rápido" href="/quickstart">
    ler o contexto com o SDK.
  </Card>

  <Card title="Receber traces OTLP" href="/api/otel-traces">
    a referência do endpoint.
  </Card>

  <Card title="Webhooks dos seus sistemas" href="/guides/system-webhooks">
    eventos de CRM, ERP e help desk.
  </Card>
</CardGroup>
