Tracing (OpenTelemetry / OTLP)
Enprompta exposes a standard OTLP/HTTP trace endpoint. Any OpenTelemetry SDK, OpenInference, or OpenLLMetry exporter can send traces with no custom code — just point your existing instrumentation at Enprompta with two environment variables.
On Node or Python? The Enprompta SDK auto-instruments OpenAI, Anthropic & Gemini in one line — see the TypeScript or Python SDK docs. This page is the vendor-neutral OTLP reference for everything else.
On this page
Instrument with a coding agent
On a Node or Python app, the fastest way to start reporting traces is to let your coding agent wire it up. Enprompta ships an open Agent Skill (the standard supported by Claude Code, Cursor, Codex, Copilot, Gemini CLI, and others) that instruments your app end to end — detect the stack (language, provider, framework, entrypoint), install the SDK, wire auto-instrumentation at the entrypoint, and verify a real trace lands.
Tracing is fail-silent by design — it never throws, so a broken setup produces no error, just no traces. The skill treats “done” as a trace you can actually see, and won't stop until it confirms one arrived.
Add the skill to your agent, set your key, then ask it to set up tracing:
# 1. Add the skill to your agent (any Skills-compatible agent).
npx skills add enprompta/enprompta-skills --skill enprompta-instrumentation
# 2. Set your key — from the dashboard → API Keys.
# The agent references the env var; it never handles the value.
export ENPROMPTA_API_KEY=ep_your_api_key
# 3. Prompt your agent:
# "Set up Enprompta tracing in my app."The skill handles the sharp edges that make setup silently no-op — ESM module patching, running init() before the provider is imported, and framework span-trees for LangChain / LlamaIndex. Prefer to wire it by hand? See the TypeScript or Python SDK guides, or the vendor-neutral OTLP reference below.
No shell, or want the check as a tool? The @enprompta/mcp-server exposes a verify_setup tool (and trace/eval tools) to any MCP-capable agent — so the agent confirms a real trace landed without leaving the conversation.
1. Endpoint & authentication
The OTLP/HTTP trace endpoint accepts the standard OTLP /v1/traces path:
POST https://enprompta.com/api/ingest/otlp/v1/tracesAuthenticate with an API key sent as a Bearer token. The key must have the traces:write scope — create one in your dashboard under API Keys.
Authorization: Bearer ep_your_api_keyBoth OTLP wire formats are supported: application/x-protobuf (the OTLP default) and application/json.
2. Quick start (any OTel exporter)
If you already use OpenTelemetry — directly, or via OpenInference or OpenLLMetry auto-instrumentation — you don't need an Enprompta SDK. Set the two standard OTLP environment variables and your traces flow to Enprompta:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://enprompta.com/api/ingest/otlp/v1/traces"
OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer ep_your_api_key"That's it — any instrumentation that emits the OpenInference (llm.*) or OpenTelemetry GenAI (gen_ai.*) semantic conventions is understood automatically (see section 4).
For example, with the Python OpenTelemetry SDK the standard OTLP HTTP exporter picks those variables up:
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()
# Reads OTEL_EXPORTER_OTLP_TRACES_ENDPOINT and _HEADERS from the environment
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
# Now add your LLM instrumentation (OpenInference / OpenLLMetry) as usual —
# its spans are exported straight to Enprompta.Prefer not to wire up OTLP? The Enprompta SDK ships an ergonomic span-tree builder — enprompta.trace().span().end() — that produces the same typed, session-grouped span trees from plain app code (bundler-safe, no exporter setup). Ideal for agents and tool loops. See the TypeScript SDK guide.
Bring your own OpenInference / OpenLLMetry
Already running OpenInference (Arize) or OpenLLMetry (Traceloop) instrumentation — or migrating from Phoenix / LangSmith / Langfuse? You don't switch libraries. Keep your instrumentation and point its OTLP exporter at Enprompta. Two ways:
Environment variables (most exporters)
Traceloop/OpenLLMetry and most OpenTelemetry exporters read the standard OTLP variables — the same two from section 2:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://enprompta.com/api/ingest/otlp/v1/traces"
OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer ep_your_api_key"In code — Python (OpenInference + OTel SDK)
Configure the exporter with Enprompta's endpoint and Bearer header, then register your instrumentor as usual:
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(
endpoint="https://enprompta.com/api/ingest/otlp/v1/traces",
headers={"Authorization": "Bearer ep_your_api_key"},
)))
# Then register your OpenInference instrumentor against this provider, e.g.:
# from openinference.instrumentation.openai import OpenAIInstrumentor
# OpenAIInstrumentor().instrument(tracer_provider=provider)In code — TypeScript (SDK helper)
The Enprompta SDK ships otlpExporterConfig(), so you don't hand-write the endpoint or header:
import { otlpExporterConfig } from '@enprompta/sdk'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
const exporter = new OTLPTraceExporter(
otlpExporterConfig({ apiKey: process.env.ENPROMPTA_API_KEY! }),
)
// register `exporter` on your TracerProvider as usualBoth conventions are understood on ingest (see section 4), and cost is priced from the model when the span omits it. To confirm it worked, watch Observability — or have your coding agent run the MCP verify_setup tool.
3. Send a trace with cURL
A minimal OTLP/JSON request — useful to verify connectivity. IDs are hex strings and timestamps are unix nanoseconds:
curl -X POST https://enprompta.com/api/ingest/otlp/v1/traces \
-H "Authorization: Bearer ep_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"resourceSpans": [{
"resource": { "attributes": [
{ "key": "enprompta.environment", "value": { "stringValue": "production" } }
]},
"scopeSpans": [{
"spans": [{
"traceId": "5b8efff798038103d269b633813fc60c",
"spanId": "eee19b7ec3c1b174",
"name": "chat",
"startTimeUnixNano": "1700000000000000000",
"endTimeUnixNano": "1700000001200000000",
"status": { "code": 1 },
"attributes": [
{ "key": "gen_ai.system", "value": { "stringValue": "openai" } },
{ "key": "gen_ai.request.model", "value": { "stringValue": "gpt-4o" } },
{ "key": "gen_ai.prompt", "value": { "stringValue": "Summarise this ticket." } },
{ "key": "gen_ai.completion", "value": { "stringValue": "The customer reports..." } },
{ "key": "gen_ai.usage.prompt_tokens", "value": { "intValue": 120 } },
{ "key": "gen_ai.usage.completion_tokens", "value": { "intValue": 48 } }
]
}]
}]
}]
}'On success the endpoint returns 200 with an OTLP ExportTraceServiceResponse (an empty partialSuccess object). Spans are persisted asynchronously and appear under Observability.
4. Semantic conventions
Enprompta reads span attributes from both the OpenInference (llm.*) and OpenTelemetry GenAI (gen_ai.*) conventions. Provide either; if both are present, llm.* wins.
| Field | OpenInference | OpenTelemetry GenAI |
|---|---|---|
| Provider | llm.provider | gen_ai.system |
| Model | llm.model | gen_ai.request.model |
| Input / prompt | llm.input.messages | gen_ai.prompt |
| Output / completion | llm.output.messages | gen_ai.completion |
| Input tokens | llm.token_count.prompt | gen_ai.usage.prompt_tokens |
| Output tokens | llm.token_count.completion | gen_ai.usage.completion_tokens |
| Latency (ms) | llm.latency_ms | — (else derived from span start/end) |
| Cost | llm.cost | — (else priced from model) |
Cost & latency are filled in for you. If a span omits llm.cost, Enprompta prices it from the provider/model. If it omits llm.latency_ms, latency is derived from the span's start and end times. Any attribute not in the table above is kept on the trace as metadata.
5. Linking prompts, sessions & environments
Set these enprompta.* span (or resource) attributes to connect a trace to the rest of your workspace:
| Attribute | Purpose |
|---|---|
| enprompta.environment | Environment label (defaults to "production"). |
| enprompta.prompt_id | Link the trace to a registered prompt. |
| enprompta.prompt_version_id | Link to a specific prompt version. |
| enprompta.session_id (or session.id) | Group multi-turn calls into one session. |
| enprompta.project_id | Assign the trace to a project. |
Resource-level attributes are inherited by every span in the batch (span-level values override them) — so set enprompta.environment and enprompta.project_id once on the resource.
6. Quotas, limits & responses
- Monthly trace quota. Free includes 5,000 traces/month; Pro 200,000. Over quota, the endpoint returns
403with apartialSuccess.rejectedSpanscount — a permanent (non-retryable) signal per the OTLP spec, so exporters drop the batch rather than retry. - Batch size. Up to 100 spans are accepted per request. If a batch exceeds that, the extra spans are reported in
partialSuccess.rejectedSpans; send larger volumes across multiple batches. - Errors. A span with OTLP status code
ERRORis recorded as a failed trace (its status message is preserved). - Auth failures. A missing, invalid, or expired key returns
401; a valid key that lacks thetraces:writescope returns403.
Prefer a managed client? The Python and TypeScript SDKs can also send traces via the native /api/v1/traces endpoint.