Skip to main content
If your app already uses OpenTelemetry (OTel), you can send its traces straight to Glassray. Point your OTel exporter at Glassray’s endpoint, add an API key, and tag your traces. You don’t need the Glassray SDK.
Not using OpenTelemetry yet? For JavaScript and TypeScript, the @glassray/tracing SDK is quicker to set up. It sends to the same endpoint without any OTel setup.

Setup

1

Create an API key

In Glassray, go to Settings → Sources and add an OpenTelemetry source. Copy the API key it shows you. You only see it once.Store it where your app can read it, for example as the environment variable GLASSRAY_API_KEY.
2

Set up OTel and point it at Glassray

Set up the OTel SDK once, when your app starts. Its exporter needs two things: Glassray’s URL, https://app.glassray.ai/api/public/otel/v1/traces, and the header Authorization: Bearer <your-api-key>.
tracing.ts
Before a script exits, call await provider.shutdown(). In a serverless function, call await provider.forceFlush() at the end of each request. Otherwise the last batch of spans is lost.This exporter sends protobuf. @opentelemetry/exporter-trace-otlp-http sends JSON, and works the same way.
Prefer environment variables? You can leave the URL and key out of the code and set them as environment variables instead. The names are the same in every language:
Then create the exporter without a URL or headers, for example new OTLPTraceExporter() in Node.js or OTLPSpanExporter() in Python. You still need the rest of the setup code above.Want the same traces in another tool too? See Send traces to more than one tool.
3

Tag your traces

Tags go on two kinds of span:
  • The root span, one per run. It says who the run was for and what it was asked. glassray.agent is already set for the whole app by the setup code in the previous step.
  • A child span for each step, such as an LLM call. It says which model ran, how many tokens it used, and what went in and came out.
The example below handles a support ticket with one LLM call. It uses the Anthropic SDK, but the attribute names are the same for any provider. Every attribute is explained in a comment, and they’re all listed in What Glassray reads from your spans.
Nested spans don’t need a parent passed in. A span you start while another is active becomes its child, even if the code is in another function or file.For prompt-cache tokens, a cost you already know, or other providers’ token conventions, see Usage and cost attributes.
This step always needs code. Environment variables and a Collector only change where traces are sent. They can’t add tags that change per run, like glassray.customer, because only your app knows which customer a run is for.
GenAI instrumentation libraries such as OpenLIT, OpenLLMetry and OpenInference build on OTel and can add model and token attributes to your LLM spans. They don’t add Glassray’s tags. See What Glassray reads from your spans for the full list.
4

Check that traces arrive

Run your app so it creates at least one trace, then open Traces in Glassray. If nothing shows up, check your exporter’s logs for one of the error codes.

Endpoint reference

  • Format: OTLP over HTTP, as protobuf (Content-Type: application/x-protobuf) or JSON (Content-Type: application/json). Most OTel SDKs send protobuf by default. Both work the same.
  • Compression: gzip (Content-Encoding: gzip) or none.
  • gRPC: not supported.
  • API key: must have the traces:write permission. Keys from an OpenTelemetry source have it.

What Glassray reads from your spans

Root-span attributes override resource attributes with the same name. Any other string, number or boolean attribute on the root span or the resource (such as report_id) becomes a filter in the Traces view. Attributes on child spans don’t, and neither do OTel namespaces such as service.* or http.*. Glassray keeps up to 48 of these per trace, with values up to 256 characters. For the full list, including prompt-cache tokens and cost, see Bring your own OTel.

How ingest behaves

Success

A 200 means the batch was received. The response uses the same Content-Type as the request.

Safe to retry

Sending the same spans again doesn’t create duplicates.

Batches are combined

Spans from the same trace sent in separate requests are merged into one trace. If your source has filters, see Limits.

Limits

  • Requests larger than 32 MB are rejected with 413.
  • A single trace larger than 16 MiB is skipped. The rest of the batch is still stored.
  • If your source has filters, traces that don’t match them aren’t stored. The request still returns 200.
  • Filters are checked on each request on its own. When an exporter sends a long run in several requests, a request that only has child spans is matched on a child span’s name. With a filter on the trace name, those child spans can be dropped before the root span arrives.

Error codes

Send traces to more than one tool

If you want the same traces in Glassray and another tool, for example Langfuse, run an OTel Collector. It’s a separate service that receives traces from your apps and forwards them to several tools at once.
otel-collector-config.yaml
Point your app’s exporter at the Collector instead of Glassray, for example http://localhost:4318/v1/traces, with no API key. The Collector adds each tool’s key when it forwards traces. Use the otlphttp exporter, not the gRPC otlp exporter: Glassray only accepts HTTP.