OpenTelemetry (OTLP) Ingest & Query

Temps ingests OpenTelemetry traces, metrics, and logs over OTLP/HTTP and stores them in TimescaleDB, then exposes authenticated query endpoints that power the monitoring UI. Use it when you want to point any OTel SDK or the OpenTelemetry Collector at your Temps instance instead of running a separate observability backend.


Overview

OTel support is provided by the temps-otel plugin crate. It accepts protobuf-encoded OTLP payloads (with gzip or zstd compression) for metrics, traces, and logs, persists them to TimescaleDB hypertables, and serves read endpoints for the console.


How It Works

  1. An OTel SDK or Collector exports OTLP/HTTP protobuf to one of the ingest routes below.
  2. The ingest handler authenticates the request, decompresses the body (gzip/zstd), and decodes the protobuf.
  3. Telemetry is written to TimescaleDB hypertables, associated with a project (and optionally environment and deployment).
  4. The console reads it back through the authenticated query endpoints, including GenAI trace summaries derived from gen_ai.* span attributes.

There are two ingest styles:

  • Header-authenticated — the project (and environment/deployment) are resolved from the token. Best for app-level SDK instrumentation.
  • Path-scoped — the project, environment, and deployment IDs are carried directly in the URL path. Useful when one credential ships telemetry for many targets.

Ingest Endpoints

All ingest endpoints are POST, accept OTLP/HTTP protobuf bodies, and support Content-Encoding: gzip or zstd.

Header-authenticated

MethodPathSignal
POST/api/otel/v1/metricsMetrics
POST/api/otel/v1/tracesTraces
POST/api/otel/v1/logsLogs

Path-scoped

The project, environment, and deployment IDs are embedded in the path:

MethodPathSignal
POST/api/otel/v1/{project_id}/{environment_id}/{deployment_id}/metricsMetrics
POST/api/otel/v1/{project_id}/{environment_id}/{deployment_id}/tracesTraces
POST/api/otel/v1/{project_id}/{environment_id}/{deployment_id}/logsLogs

Authentication

Ingest requests read the token from either header:

HeaderValue
AuthorizationBearer <token>
X-Temps-Api-Key<token>

Two token types are accepted:

PrefixTypeExtra requirement
tk_API keyAlso send X-Temps-Project-Id so the key resolves to a project
dt_Deployment tokenCarries its own project/environment/deployment association

Which one should I use? Use tk_ API keys when you want to route telemetry for a single project from one exporter. Use dt_ deployment tokens when you're shipping telemetry from multiple deployments or environments through the same exporter — the token already knows which project, environment, and deployment it belongs to, so no extra header is needed.


Query Endpoints

These power the monitoring UI and the unified Observe page, which merges these traces with requests, errors, and revenue events into one stream. Every one requires authentication plus the OtelRead permission. All are GET. Endpoints that take a {project_id} carry it in the path; the others accept project_id as a query parameter.

MethodPathReturns
GET/api/otel/metricsMetric data points
GET/api/otel/metric-names/{project_id}Distinct metric names for a project
GET/api/otel/tracesTrace list
GET/api/otel/trace-summariesAggregated trace summaries
GET/api/otel/traces/{project_id}/{trace_id}Spans for a single trace
GET/api/otel/logsLog records
GET/api/otel/insights/{project_id}Computed insights for a project
GET/api/otel/health/{project_id}Health view for a project
GET/api/otel/quota/{project_id}Storage quota usage for a project
GET/api/otel/pipeline-statsIngest pipeline statistics
GET/api/otel/genai/tracesGenAI trace summaries (see below)
GET/api/otel/genai/traces/{project_id}/{trace_id}A single GenAI trace

GenAI Trace Summaries

The GenAI views are derived from regular OTel spans — there is no separate ingest path. A span is treated as a GenAI span when its attributes contain either gen_ai.system or gen_ai.provider.name. The provider shown in the UI is taken from gen_ai.provider.name, falling back to the deprecated gen_ai.system when the newer attribute is absent.

This means any OTel SDK that emits OpenTelemetry gen_ai.* semantic-convention spans (for example, AI SDK instrumentation) shows up automatically in GET /api/otel/genai/traces once those spans land via the normal trace ingest routes.


Configuration

All configuration is via TEMPS_OTEL_* environment variables. Every one is optional and has a default — OTel ingest works out of the box with none of them set.

VariableDefaultPurpose
TEMPS_OTEL_RATE_LIMIT1000Max ingest requests per project per window
TEMPS_OTEL_RATE_LIMIT_WINDOW_SECS60Rate-limit window length, in seconds
TEMPS_OTEL_RETENTION_DAYS7How long telemetry is retained
TEMPS_OTEL_S3_REGION(unset)S3 region for optional log archival
TEMPS_OTEL_S3_ENDPOINT(unset)Custom S3 endpoint (e.g. for S3-compatible storage)
TEMPS_OTEL_S3_ACCESS_KEY(unset)S3 access key
TEMPS_OTEL_S3_SECRET_KEY(unset)S3 secret key
TEMPS_OTEL_S3_BUCKET(unset)S3 bucket for archived logs
TEMPS_OTEL_S3_PREFIXotel-logsKey prefix for archived log objects
TEMPS_OTEL_QUOTA_GB10Per-project storage quota, in GB
TEMPS_OTEL_ENABLE_HEALTH_COMPUTEtrueRun the background health-compute task
TEMPS_OTEL_ENABLE_ANOMALY_DETECTIONtrueRun the background anomaly-detection task

When a project exceeds its rate limit, ingest returns 429 Too Many Requests and the error reports the limiter's actual configured value (not a hardcoded number), so if you raise TEMPS_OTEL_RATE_LIMIT the message reflects the new limit.


Example: send a trace

The most common setup is to point the OpenTelemetry Collector (or any OTLP/HTTP exporter) at your Temps instance. The exporter must use the HTTP/protobuf OTLP protocol.

OpenTelemetry Collector — otlphttp exporter

exporters:
  otlphttp/temps:
    # Base endpoint; the collector appends /v1/traces, /v1/metrics, /v1/logs
    endpoint: https://your-temps-instance.com/api/otel
    headers:
      Authorization: "Bearer dt_your-deployment-token"
    compression: gzip

service:
  pipelines:
    traces:
      exporters: [otlphttp/temps]
    metrics:
      exporters: [otlphttp/temps]
    logs:
      exporters: [otlphttp/temps]

With a tk_ API key instead of a deployment token, also send the project ID:

cURL — header-authenticated trace ingest (API key)

# Body must be an OTLP ExportTraceServiceRequest encoded as protobuf.
curl -X POST https://your-temps-instance.com/api/otel/v1/traces \
  -H "Authorization: Bearer tk_your-api-key" \
  -H "X-Temps-Project-Id: 42" \
  -H "Content-Type: application/x-protobuf" \
  -H "Content-Encoding: gzip" \
  --data-binary @trace.pb.gz

You don't need a Collector in front of Temps — any OTel SDK can export directly over OTLP/HTTP. With Python's opentelemetry-exporter-otlp-proto-http, point the exporter at the full traces path and pass the token as a header:

Python — export spans directly with the OTLP/HTTP SDK

# pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
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

exporter = OTLPSpanExporter(
    endpoint="https://your-temps-instance.com/api/otel/v1/traces",
    headers={"Authorization": "Bearer dt_your-deployment-token"},
)

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("example-app")
with tracer.start_as_current_span("hello"):
    print("span sent to Temps")

With a tk_ API key instead of a deployment token, add the project header to the exporter — pass headers={"Authorization": "Bearer tk_your-api-key", "X-Temps-Project-Id": "42"}.

Then read traces back from the authenticated query surface:

cURL — list traces (requires OtelRead)

curl "https://your-temps-instance.com/api/otel/traces?project_id=42" \
  -H "Authorization: Bearer <temps-api-key-with-OtelRead>"

Notes & Gotchas

  • Protobuf only. Ingest accepts OTLP/HTTP with protobuf-encoded bodies. Configure your exporter for the http/protobuf protocol, not grpc and not OTLP/JSON.
  • Use the /api prefix. Routes are served under /api/otel/.... Hitting /otel/... directly falls through to the SPA.
  • tk_ keys need X-Temps-Project-Id. API keys must include the project header on ingest; deployment tokens (dt_) already carry their project/environment/deployment context.
  • Query endpoints need OtelRead. They are the console's authenticated read surface, distinct from the header-token ingest path.
  • GenAI views are derived, not ingested. They come from trace spans carrying gen_ai.system / gen_ai.provider.name attributes — there is no separate GenAI ingest endpoint.
  • Storage is TimescaleDB. OTel data lands in TimescaleDB hypertables, so a TimescaleDB-enabled control plane database is required.

Once telemetry is flowing, these pages cover the surfaces that read it back:

  • Observe — the unified, time-ordered stream that merges OTel traces with requests, errors, and revenue events for a project.
  • Monitoring — uptime checks and alerts on top of the telemetry you ingest here.
  • Logs & Debugging — viewing and searching log records.
  • Errors — error tracking and grouping.

Was this page helpful?