Custom applications

OpenTelemetry (OTel)

Send traces from any OTel-instrumented AI framework directly to Voxmith

Voxmith runs a hosted OTLP collector at otel.voxmith.com. Any framework that emits OpenTelemetry spans can ship them straight to Voxmith: no SDK required.

Your AI App  →  otel.voxmith.com  →  Voxmith Dashboard

Quick Setup

Set two environment variables before starting your app:

OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.voxmith.com
OTEL_EXPORTER_OTLP_HEADERS="X-Voxmith-Org-ID=<your-org-id>"

Get your org ID from app.voxmith.com.

For SDKs that take an explicit URL, use https://otel.voxmith.com/v1/traces.

Framework Guides

Custom Tool Spans

If your stack doesn't have a framework integration, wrap each agent / tool call in an OTel span. Name the span with a tool. prefix so Voxmith classifies it as a tool call.

1. Install

Already have an OTel SDK and the OTLP exporter installed? Skip.

No OTel yet?

pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http

2. Wire the OTLP exporter pointing at Voxmith

Already have an OTel TracerProvider? Append Voxmith as an additional span processor:

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

trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(
            endpoint="https://otel.voxmith.com/v1/traces",
            headers={"X-Voxmith-Org-ID": os.environ["VOXMITH_ORG_ID"]},
        )
    )
)

No OTel yet? Full setup:

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

provider = TracerProvider(resource=Resource.create({"service.name": "my-app"}))
provider.add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(
            endpoint="https://otel.voxmith.com/v1/traces",
            headers={"X-Voxmith-Org-ID": os.environ["VOXMITH_ORG_ID"]},
        )
    )
)
trace.set_tracer_provider(provider)

3. Wrap each tool call in a span with userId / sessionId

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("tool.search_web") as span:
    span.set_attribute("voxmith.user_id", "user-42")
    span.set_attribute("voxmith.session_id", "conv-abc123")
    span.set_attribute("input", query)
    try:
        result = search_web(query)
        span.set_attribute("output", str(result))
    except Exception as exc:
        span.record_exception(exc)
        raise

Additional properties

Voxmith recognizes these OTel fields and preserves non-reserved custom span attributes as event metadata:

Type Fields
Parent span parentSpanId on the child span
GenAI convention attributes gen_ai.request.model, gen_ai.response.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
Custom metadata customer.tier, feature.name, or another non-reserved OTel attribute

Note: If you don't set voxmith.session_id or another supported session attribute, Voxmith uses the trace ID as the session ID. Each trace therefore appears as a separate session. Reuse a stable session ID to group multiple traces into one conversation.

Send a trace with curl

This sends one completion span with its input/output and GenAI attributes, plus a child tool.get_weather span with its own input/output:

curl https://otel.voxmith.com/v1/traces \
  -X POST \
  -H "X-Voxmith-Org-ID: $VOXMITH_ORG_ID" \
  -H "Content-Type: application/json" \
  --data-raw '{
    "resourceSpans": [{
      "resource": {
        "attributes": [{
          "key": "service.name",
          "value": { "stringValue": "support-agent" }
        }]
      },
      "scopeSpans": [{
        "scope": { "name": "custom-otel-example" },
        "spans": [{
          "traceId": "0102030405060708090a0b0c0d0e0f10",
          "spanId": "0102030405060708",
          "name": "chat.completion",
          "startTimeUnixNano": "1700000000000000000",
          "endTimeUnixNano": "1700000002000000000",
          "attributes": [{
            "key": "voxmith.user_id",
            "value": { "stringValue": "user-42" }
          }, {
            "key": "voxmith.session_id",
            "value": { "stringValue": "conversation-abc123" }
          }, {
            "key": "input",
            "value": { "stringValue": "What is the weather in San Francisco?" }
          }, {
            "key": "output",
            "value": { "stringValue": "It is 72°F and sunny." }
          }, {
            "key": "gen_ai.request.model",
            "value": { "stringValue": "gpt-5" }
          }, {
            "key": "gen_ai.response.model",
            "value": { "stringValue": "gpt-5-2025-08-07" }
          }, {
            "key": "gen_ai.usage.input_tokens",
            "value": { "intValue": "120" }
          }, {
            "key": "gen_ai.usage.output_tokens",
            "value": { "intValue": "48" }
          }, {
            "key": "customer.tier",
            "value": { "stringValue": "enterprise" }
          }]
        }, {
          "traceId": "0102030405060708090a0b0c0d0e0f10",
          "spanId": "1112131415161718",
          "parentSpanId": "0102030405060708",
          "name": "tool.get_weather",
          "startTimeUnixNano": "1700000000200000000",
          "endTimeUnixNano": "1700000001800000000",
          "attributes": [{
            "key": "voxmith.session_id",
            "value": { "stringValue": "conversation-abc123" }
          }, {
            "key": "input",
            "value": { "stringValue": "San Francisco" }
          }, {
            "key": "output",
            "value": { "stringValue": "72°F and sunny" }
          }, {
            "key": "feature.name",
            "value": { "stringValue": "weather" }
          }]
        }]
      }]
    }]
  }'

A successful export returns HTTP 200.

What appears in Voxmith

  • Events: every received span/event.
  • Conversations: spans grouped by voxmith.session_id, session.id, or framework-specific session metadata.
  • Tool calls: spans named with tool.* or framework tool-call attributes.
  • Errors: spans marked failed or exceptions recorded on spans.

Verify

  1. Start your app with the OTel exporter configured.
  2. Run one agent turn or tool call.
  3. Open app.voxmith.com.
  4. Check Events first, then Conversations or Tool calls.

Troubleshooting

  • No data: confirm the endpoint is https://otel.voxmith.com/v1/traces when your SDK expects a trace URL.
  • Wrong org: confirm X-Voxmith-Org-ID is attached to the OTLP exporter.
  • No input/output text: your framework may not export message content by default, or you may have disabled it.

Next steps

  • Events: verify the spans Voxmith received.
  • Tool calls: inspect tool reliability and latency.
  • Intents: organize production conversations by what users wanted.