Frametail
Observability

OTLP-native trace ingest

Send traces from any OpenTelemetry exporter without the Frametail SDK.

Frametail accepts traces via the OpenTelemetry Protocol (OTLP), enabling any standard exporter — OpenLLMetry, OpenInference, Vercel AI SDK's experimental_telemetry, LangChain, or your own instrumentation — to send traces without installing the Frametail SDK.

Sending traces

Send an OTLP trace export to:

POST https://api.frametail.io/v1/traces/otlp
Authorization: Bearer <API_KEY>
Content-Type: application/json

The request body is an OTLP ExportTraceServiceRequest in JSON format. Frametail parses the trace, extracts Gen AI semantics from span attributes, and stores it exactly as if you'd sent it via the SDK.

Example: OpenLLMetry with Frametail exporter

If you're using OpenLLMetry (Node.js), configure the Frametail OTLP collector:

import { NodeSDK } from '@opentelemetry/sdk-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'

const sdk = new NodeSDK({
  instrumentations: [getNodeAutoInstrumentations()],
  traceExporter: new OTLPTraceExporter({
    url: 'https://api.frametail.io/v1/traces/otlp',
    headers: {
      authorization: `Bearer ${process.env.FRAMETAIL_API_KEY}`,
    },
  }),
})

sdk.start()
console.log('OpenTelemetry traces configured → Frametail')

// Your app now auto-exports traces

Example: Vercel AI SDK with OTEL telemetry

If you're using the Vercel AI SDK's built-in OpenTelemetry, point it at Frametail:

import { generateText } from 'ai'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { BasicTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'

const exporter = new OTLPTraceExporter({
  url: 'https://api.frametail.io/v1/traces/otlp',
  headers: {
    authorization: `Bearer ${process.env.FRAMETAIL_API_KEY}`,
  },
})

const tracerProvider = new BasicTracerProvider()
tracerProvider.addSpanProcessor(new SimpleSpanProcessor(exporter))
const tracer = tracerProvider.getTracer('my-app')

const { text } = await generateText({
  model: 'openai/gpt-4o-mini',
  prompt: 'Summarize: ...',
  experimental_telemetry: {
    isEnabled: true,
    tracer,
  },
})

Span & attribute mapping

Frametail automatically extracts:

  • Span type — Derived from gen_ai.operation.name (e.g., chatllm, generate_contentcompose).
  • Model — From gen_ai.request.model.
  • Tokens — From gen_ai.usage.input_tokens and gen_ai.usage.output_tokens.
  • Input/output — From gen_ai.input.messages and gen_ai.output.messages (JSON arrays).
  • Status — OTLP status.code (0 = UNSET, 1 = OK, 2 = ERROR).

All other span attributes are preserved and available for filtering, searching, and rule-matching.

Project routing

Just as with the SDK, traces are routed to your project via your API key. Set the project in your dashboard under SettingsAPI Keys.

Rate limits & quotas

OTLP ingest follows the same rate limits as the JSON API:

  • Standard tier: 1,000 spans/minute
  • Pro tier: 10,000 spans/minute
  • Enterprise: Custom

Exceeding the limit returns a 429 Too Many Requests response.

Debugging

If traces don't appear:

  1. Check your API key — The Authorization header must be a valid Frametail API key.
  2. Verify JSON validity — OTLP requests must be valid JSON with resourceSpans and scopeSpans arrays.
  3. Inspect the response — Frametail returns { traceIds: [...], spanCount: N } on success, or an error message on failure.
  4. Check project routing — Traces go to the project associated with your API key.

Example curl to test:

curl -X POST https://api.frametail.io/v1/traces/otlp \
  -H "Authorization: Bearer $FRAMETAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "resourceSpans": [{
      "scopeSpans": [{
        "spans": [{
          "traceId": "abc123",
          "spanId": "def456",
          "name": "generate",
          "startTimeUnixNano": "1718731200000000000",
          "endTimeUnixNano": "1718731201000000000",
          "attributes": [
            {"key": "gen_ai.operation.name", "value": {"stringValue": "chat"}},
            {"key": "gen_ai.request.model", "value": {"stringValue": "openai/gpt-4o"}},
            {"key": "gen_ai.input.messages", "value": {"stringValue": "[{\"role\":\"user\",\"content\":\"hi\"}]"}},
            {"key": "gen_ai.output.messages", "value": {"stringValue": "[{\"role\":\"assistant\",\"content\":\"hello\"}]"}}
          ],
          "status": {"code": 1}
        }]
      }]
    }]
  }'