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

# Empieza a enviar trazas a Zelto

> Crea una clave de ingestión, exporta tu primera traza, identifica agentes con tus propios IDs y vincula las trazas a las llamadas.

Envía una traza de prueba, encuéntrala en Zelto y aplica la misma configuración
a tu agente. Una **traza** representa una llamada o sesión; sus **spans**
representan pasos con duración, como reconocimiento de voz, una petición al LLM,
una invocación de herramienta o síntesis de voz. Exportar trazas no carga la
transcripción ni la grabación: esos datos llegan mediante la
[integración de llamadas](/es/docs/integrations).

Necesitas una organización de Zelto, un `owner` o `admin` que cree la clave y un
servidor o worker con acceso HTTPS a `ingest.zelto.ai`. El primer ejemplo usa
Python 3.10+ y datos sintéticos; no realiza una llamada.

## 1. Crea una clave de telemetría

En la organización de destino, abre **Trazas → Conectar agente** o
**Configuración → Integraciones → Observabilidad**. Crea una **clave de
ingestión de observabilidad** y cópiala cuando se muestre. Es una credencial
de solo escritura para esa organización. Guárdala en los secretos o variables
de entorno del worker como `ZELTO_INGEST_KEY`.

Es independiente de la clave REST/MCP que utilizas para cargar conversaciones.
La telemetría usa `Authorization: Bearer <ingest-key>` y no necesita el encabezado
`X-Zelto-Provider`. Conserva la clave en el servidor.

## 2. Exporta una primera traza

Crea un entorno de Python aislado e instala el SDK y el exportador HTTP:

```bash theme={null}
python3 -m venv .venv
source .venv/bin/activate
python -m pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
export ZELTO_INGEST_KEY="<your-observability-ingest-key>"
```

Guarda lo siguiente como `send_trace.py` y reemplaza el marcador de la clave en
el entorno antes de ejecutarlo. El ejemplo representa un solo agente, así que
su identidad estable está en el **recurso**: los atributos compartidos por
todos los spans de este proveedor de trazas.

```python theme={null}
import os
import uuid

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

provider = TracerProvider(resource=Resource.create({
    "service.name": "zelto-trace-demo",
    "zelto.agent_external_id": "support-agent",
    "zelto.agent_provider": "zelto",
}))
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(
    endpoint="https://ingest.zelto.ai/v1/traces",
    headers={"Authorization": f"Bearer {os.environ['ZELTO_INGEST_KEY']}"},
)))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("zelto-getting-started")
call_id = f"demo-{uuid.uuid4().hex}"

try:
    with tracer.start_as_current_span("demo.call", attributes={"session.id": call_id}) as root:
        with tracer.start_as_current_span("demo.tool", attributes={
            "session.id": call_id,
            "tool.name": "check_availability",
        }):
            pass
        print(f"trace_id={root.get_span_context().trace_id:032x}")
        print(f"session.id={call_id}")
    provider.force_flush(timeout_millis=10_000)
finally:
    provider.shutdown()
```

```bash theme={null}
python send_trace.py
```

Verás un ID de traza y un ID de sesión único. El span hijo comparte el ID de
traza del raíz porque se crea dentro de su contexto activo. Los atributos como
`session.id` **no se heredan automáticamente**: agrégalos a cada span relevante
o mediante el mecanismo de metadatos de tu framework. El script vacía el lote
de spans terminados antes de salir. Revisa también los errores del exportador:
imprimir un ID o terminar el vaciado no demuestra por sí solo que el servidor
aceptó los datos.

## 3. Encuéntrala en Zelto

Abre **Trazas** en la misma organización, borra los filtros de agente y empresa
y elige un periodo que incluya el momento actual. Busca `demo.call` y compara su
ID con el que imprimió el script. Ábrela para ver `demo.tool` debajo del span
raíz e inspeccionar sus atributos. Zelto reutiliza o crea el agente `support-agent`.

Una respuesta HTTP correcta confirma la aceptación. En organizaciones con
ingestión en cola, los datos se pueden consultar después del procesamiento
asíncrono; el objetivo es **30 segundos desde la aceptación con carga normal**,
no un máximo garantizado. El exportador añade su propia espera para agrupar
lotes antes de la aceptación, y una interrupción puede alargar el procesamiento.
Actualiza la vista antes de diagnosticar una traza ausente. Los paneles de la
llamada vinculada también requieren que haya llegado la conversación.

Este ejemplo sintético aparece **sin conversación vinculada** hasta que cargues
una llamada con el mismo ID externo. Eso no indica un fallo de exportación.

## Identifica agentes y llamadas de forma coherente

| Identificador | Uso | Duración |
| - | - | - |
| `zelto.agent_external_id` | ID del agente en tu sistema; usa el mismo valor que `agent.externalId` en la carga de llamadas. | Estable entre llamadas y versiones. |
| `zelto.agent_provider` | El mismo proveedor que en la ingestión de llamadas, por ejemplo `livekit`. | Estable para esa integración. |
| `session.id` | ID de llamada, sesión o sala; debe coincidir con `call.externalId`. | Único por llamada y constante en los reintentos. |
| ID de traza / ID de span | Identificadores de OpenTelemetry generados por el SDK. | Una traza por sesión y un span por operación; constantes en los reintentos. |

Zelto busca los IDs externos **dentro de tu organización**. Los IDs nuevos crean
agentes automáticamente, con el ID externo como nombre inicial. Si omites el
proveedor, Zelto reutiliza una coincidencia única o crea el agente bajo `zelto`.
Envía `livekit` cuando las llamadas usen `X-Zelto-Provider: livekit` para evitar
identidades separadas si la telemetría llega primero. Si el ID coincide con
varios agentes de distintos proveedores, queda sin asignar: especifica el
proveedor para eliminar la ambigüedad.

Los IDs externos distinguen mayúsculas y minúsculas y admiten hasta 255
caracteres; los nombres de proveedor, hasta 50. Las identidades fusionadas
apuntan al agente que permanece. Si no puede resolver una identidad externa,
Zelto no la sustituye por el agente de otra llamada que coincida con la sesión.
Consulta las [reglas de identidad](/es/docs/traces#identifica-agentes-con-tus-propios-ids).

Si el proceso atiende a un solo agente, usa atributos del recurso. Si atiende
a varios, pon la identidad en **cada span y registro**; estos atributos tienen
prioridad sobre los del recurso. No uses un ID de sala ni una versión como ID
del agente. `zelto.agent_id` y el campo nativo `agentId` significan un **UUID de
Zelto** y tienen prioridad. Aunque tu ID tenga formato UUID, envíalo en
`zelto.agent_external_id`.

Envía los datos de la llamada mediante tu [integración](/es/docs/integrations)
o la [API de carga](/es/docs/integrations/api-call-upload), con la misma
organización, identidad del agente, proveedor e ID externo de llamada. La traza
y la llamada pueden llegar en cualquier orden. El encabezado muestra **Abrir
conversación** cuando encuentra una coincidencia única; así se habilitan la
grabación, la transcripción y los resultados de monitores disponibles. Consulta
[cómo vincular conversaciones](/es/docs/traces#vincular-trazas-a-conversaciones).

## Conecta tu exportador existente

Si tu aplicación ya inicializa OpenTelemetry, agrega el exportador de Zelto al
proveedor existente en vez de registrar otro proveedor global. Configura
**OTLP/HTTP**; no se admite gRPC en el puerto `4317`.

Si el exportador lee las variables estándar de OpenTelemetry:

```bash theme={null}
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.zelto.ai"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20${ZELTO_INGEST_KEY}"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
```

El endpoint base compartido **no lleva `/v1/traces`**: el SDK añade la ruta de
cada señal. Un endpoint específico por señal o una URL explícita del exportador
es la **URL completa**, como en el ejemplo de Python:

| Señal | URL completa | Codificación aceptada |
| - | - | - |
| Trazas | `https://ingest.zelto.ai/v1/traces` | OTLP/HTTP protobuf o JSON OTLP |
| Registros | `https://ingest.zelto.ai/v1/logs` | OTLP/HTTP protobuf o JSON OTLP |
| Spans y registros nativos | `https://ingest.zelto.ai/webhooks/otel` | JSON nativo de Zelto |

Usa `Content-Type: application/x-protobuf` para protobuf y `application/json`
para JSON. Mantén el exportador HTTP de Python en `http/protobuf`. No hay un
endpoint de métricas de telemetría. Consulta la [documentación del exportador
de OpenTelemetry para Python](https://opentelemetry.io/docs/languages/python/exporters/).

Las variables de entorno por sí solas no instrumentan el código: inicializa el
SDK y el exportador, crea spans o activa la instrumentación del framework, y
termina los spans para poder exportarlos. Usa un procesador por lotes durante
la vida del worker y vacíalo al apagarlo de forma controlada, no tras cada span.
Propaga el contexto de la traza al pasar entre tareas asíncronas o servicios.

### LiveKit

Aplica la [configuración de LiveKit](/es/docs/integrations/livekit#transmitir-trazas-opentelemetry)
al principio del entrypoint, antes de `session.start()`. Registra el proveedor
de LiveKit, añade el ID externo del agente y la referencia de sala y vacía los
spans pendientes al apagar el worker. Usa el mismo ID estable de agente y nombre
de sala que en la carga de la sesión terminada. LiveKit genera los spans de la
sesión; enviar la transcripción sigue siendo un paso independiente.

### Registros

Un exportador de trazas no envía automáticamente los registros de la aplicación.
Configura un **exportador de logs OTLP/HTTP** y la integración de logging del
SDK con `https://ingest.zelto.ai/v1/logs` y la misma clave Bearer. Emite los
registros mientras esté activo el span correspondiente para incluir sus IDs
de traza y span. Añade también la identidad del agente al registro o al recurso.
Zelto muestra los registros correlacionados en **Registros** del span y los de
toda la traza en **Resumen de la traza → Registros**. También puedes utilizar el
ejemplo nativo siguiente.

## JSON nativo sin SDK de OpenTelemetry

Usa `/webhooks/otel` cuando tu plataforma no pueda emitir OTLP. Su estructura
JSON es distinta de JSON OTLP. Genera marcas de tiempo actuales e identidades
nuevas para cada sesión; guarda el archivo sin cambios si necesitas reintentar:

```bash theme={null}
python3 - <<'PY' > telemetry.json
import json
import time
import uuid

now = time.time_ns() // 1_000_000
trace_id = uuid.uuid4().hex
span_id = uuid.uuid4().hex[:16]
identity = {"zelto.agent_external_id": "support-agent", "zelto.agent_provider": "zelto"}
print(json.dumps({
    "spans": [{
        "traceId": trace_id, "spanId": span_id, "name": "native.call",
        "type": "session", "startMs": now - 1000, "endMs": now,
        "status": "ok", "sessionRef": f"demo-{trace_id}",
        "attributes": identity,
    }],
    "logs": [{
        "traceId": trace_id, "spanId": span_id, "timestampMs": now,
        "severityNumber": 9, "severityText": "INFO", "body": "Session completed",
        "attributes": identity,
    }],
}))
PY
curl --fail-with-body -i https://ingest.zelto.ai/webhooks/otel \
  -H "Authorization: Bearer ${ZELTO_INGEST_KEY}" \
  -H "Content-Type: application/json" \
  --data-binary @telemetry.json
```

Respuesta esperada: HTTP `200` con `{"received":true}`. Los endpoints OTLP
devuelven HTTP `200` con `{}`. Las marcas de tiempo nativas usan **milisegundos
Unix**; OTLP usa **nanosegundos Unix**. Usa `parentSpanId` para spans hijos
nativos y el mismo `traceId` para agruparlos en un árbol. Los `attributes` nativos
admiten valores de texto, número y booleano.

## Límites, reintentos y solución de problemas

Cada solicitud admite **10 MB (10.000.000 bytes) después de descomprimir**.
Ajusta el lote según el tamaño de tus spans: un límite de filas no limita los
bytes. Conserva los IDs, las marcas de tiempo y el cuerpo original al reintentar.
Enviar la misma llamada con nuevos IDs de traza genera trazas separadas.

| Síntoma | Qué comprobar o hacer |
| - | - |
| `401` | Usa una clave de ingestión de observabilidad de la organización correcta; comprueba el encabezado Bearer y si se revocó la clave. |
| `400` | Corrige el cuerpo o la codificación; no reenvíes repetidamente datos inválidos. JSON nativo y JSON OTLP son esquemas distintos. |
| `413` | Divide el lote para que cada solicitud quede por debajo de 10 MB sin comprimir. |
| `429` | Respeta `Retry-After`, reduce la frecuencia o agrupa mejor los lotes; contacta con Zelto para ampliar el límite. |
| `502`, `503`, `504`, espera agotada o fallo de conexión | Conserva el lote y reintenta con espera exponencial y variación aleatoria; respeta `Retry-After` si está presente. |
| `404` o fallo al conectar al puerto `4317` | Usa el host HTTPS de ingestión y la ruta HTTP correcta, no el panel ni gRPC. |
| Aceptada, pero no visible | Espera el procesamiento, actualiza, borra filtros y comprueba organización, fechas y que los spans terminaron y se exportaron. |
| Agente incorrecto o ausente | Revisa el par ID externo/proveedor en cada span o recurso y elimina un `agentId` interno enviado por error. |
| Sin conversación vinculada | Carga la llamada por separado con el mismo `call.externalId` e identidad del agente; comprueba referencias externas duplicadas. |
| No aparecen registros | Configura un exportador de logs además del de trazas y conserva el contexto. |
| Faltan los últimos spans | Vacía y apaga el proveedor de forma controlada antes de finalizar el proceso. |

Los reintentos y las colas en memoria del SDK tienen límites: un proceso que
falla, una cola llena o una interrupción prolongada pueden perder datos **antes
de que Zelto los acepte**. Vigila los errores y descartes del exportador; usa un
Collector con almacenamiento persistente si necesitas entrega duradera desde tu
infraestructura. La [especificación de reintentos OTLP](https://opentelemetry.io/docs/specs/otlp/#retryable-response-codes)
detalla los códigos que permiten reintentar.

Ya tienes un exportador, una identidad estable y una forma de comprobar la
entrega. Explora los datos en [Trazas](/es/docs/traces) y conecta las llamadas
terminadas con la [integración de LiveKit](/es/docs/integrations/livekit).
