Guardy
Book a Demo Login

Documentation

OpenTelemetry

Send OpenTelemetry traces to Guardy when you already have OTel instrumentation or a collector pipeline.

Guardy can ingest OpenTelemetry traces over OTLP HTTP JSON. Each trace becomes a Guardy session, and each span becomes an event in the session timeline.

When to Use OTel

Use this integration when:

  • your agent framework already emits OpenTelemetry spans
  • you have an existing OpenTelemetry Collector
  • you want vendor-neutral tracing alongside Guardy classification
  • you are migrating gradually from another tracing tool

For new Guardy-first integrations, use the native SDK instead.

Endpoint

POST https://api.tryguardy.com/api/otel/v1/traces

Authenticate with either header:

Authorization: Bearer guardy_live_xxx
x-guardy-api-key: guardy_live_xxx

Your API key needs both sessions:write and events:write scopes.

Collector Configuration

Forward traces from an OpenTelemetry Collector to Guardy:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
  batch:
    timeout: 1s
    send_batch_size: 100
    send_batch_max_size: 1000
  resource:
    attributes:
      - key: service.name
        value: my-agent
        action: upsert

exporters:
  otlphttp/guardy:
    traces_endpoint: https://api.tryguardy.com/api/otel/v1/traces
    encoding: json
    headers:
      x-guardy-api-key: ${env:GUARDY_API_KEY}

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch, resource]
      exporters: [otlphttp/guardy]

For local development with the API server on port 3001, use:

exporters:
  otlphttp/guardy:
    traces_endpoint: http://localhost:3001/api/otel/v1/traces
    encoding: json
    headers:
      x-guardy-api-key: ${env:GUARDY_API_KEY}

Add these attributes to the root span when possible:

AttributePurpose
guardy.agent.nameAgent name in Guardy
guardy.user_id or user.idUser grouping
guardy.convo_id or conversation.idConversation grouping
guardy.inputSession input
guardy.outputSession output
guardy.costEstimated run cost
guardy.experiment.assignment_idLinks the trace to a prompt variant assignment

Guardy also maps common GenAI semantic convention attributes, including:

  • gen_ai.system
  • gen_ai.request.model
  • gen_ai.usage.input_tokens
  • gen_ai.usage.output_tokens
  • gen_ai.usage.total_tokens
  • gen_ai.prompt
  • gen_ai.completion

Minimal OTLP JSON Example

curl -X POST https://api.tryguardy.com/api/otel/v1/traces \
  -H "Content-Type: application/json" \
  -H "x-guardy-api-key: guardy_live_xxx" \
  -d '{
    "resourceSpans": [
      {
        "resource": {
          "attributes": [
            { "key": "service.name", "value": { "stringValue": "support-agent" } }
          ]
        },
        "scopeSpans": [
          {
            "spans": [
              {
                "traceId": "4fd0b6131f2f4ad9b4d05cc182b1fb11",
                "spanId": "9f3c4e3b8d9a1001",
                "name": "support request",
                "kind": "SPAN_KIND_SERVER",
                "startTimeUnixNano": "1770000000000000000",
                "endTimeUnixNano": "1770000002500000000",
                "attributes": [
                  { "key": "guardy.agent.name", "value": { "stringValue": "support-agent" } },
                  { "key": "guardy.user_id", "value": { "stringValue": "user_123" } },
                  { "key": "guardy.convo_id", "value": { "stringValue": "conv_789" } },
                  { "key": "guardy.input", "value": { "stringValue": "Help me reset my password" } },
                  { "key": "guardy.output", "value": { "stringValue": "I sent a reset link." } },
                  { "key": "gen_ai.usage.input_tokens", "value": { "intValue": 1200 } },
                  { "key": "gen_ai.usage.output_tokens", "value": { "intValue": 300 } }
                ],
                "status": { "code": "STATUS_CODE_OK" }
              }
            ]
          }
        ]
      }
    ]
  }'

Prompt Variants with OTel

OpenTelemetry ingestion does not choose prompt variants by itself. Route the variant first, then attach the assignment ID to your trace.

import { guardy } from '@guardy/sdk';

guardy.configure({ apiKey: process.env.GUARDY_API_KEY! });

const assignment = await guardy.getVariant({
  experimentName: 'support-prompt-v2',
  agentName: 'support-agent',
  userId: user.id,
  convoId: conversation.id,
});

const systemPrompt = assignment.systemPrompt ?? defaultSystemPrompt;

Add assignment.assignmentId to your root span as guardy.experiment.assignment_id. Guardy will link the ingested trace to that variant and classify the run after ingest.

Classification

After Guardy ingests a trace, it marks the session complete, stores span events, and runs the same signal classification pipeline used for SDK-created sessions.

Next Steps