> ## Documentation Index
> Fetch the complete documentation index at: https://raindrop.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenTelemetry

> Send OpenTelemetry traces to Raindrop over OTLP/HTTP

Raindrop consumes OpenTelemetry Protocol (OTLP) traces over HTTP. Send trace
exports to:

```
https://api.raindrop.ai/v1/traces
```

The endpoint accepts OTLP/HTTP JSON and protobuf. Use
`Content-Type: application/json` for JSON, or
`Content-Type: application/x-protobuf` for protobuf. Gzip and deflate
request-body compression are supported for protobuf through the standard
`Content-Encoding` header. Send JSON exports uncompressed.
This endpoint supports OTLP/HTTP only; OTLP/gRPC is not supported.

<Info>This endpoint ingests OTLP traces. OTLP metrics and logs are not accepted today.</Info>

## Prerequisites

Get your write key by logging in to [app.raindrop.ai](https://app.raindrop.ai).
Send it as a bearer token:

```http theme={null}
Authorization: Bearer YOUR_WRITE_KEY
```

To route traces to a specific [project](/docs/platform/projects), include its slug:

```http theme={null}
X-Raindrop-Project-Id: support-prod
```

If you omit this header, traces go to your organization's default Production
project. A malformed slug returns `400`; an inactive or disallowed project
returns `403`. See the [HTTP API](/docs/sdk/http-api) reference for the full
project behavior table.

## Send a trace with curl

This sends one OTLP/HTTP JSON export with a model, token usage, input message,
and output message:

```bash theme={null}
curl https://api.raindrop.ai/v1/traces \
  -X POST \
  -H "Authorization: Bearer $RAINDROP_WRITE_KEY" \
  -H "X-Raindrop-Project-Id: support-prod" \
  -H "Content-Type: application/json" \
  --data-raw '{
    "resourceSpans": [{
      "resource": {
        "attributes": [{
          "key": "service.name",
          "value": { "stringValue": "checkout-agent" }
        }]
      },
      "scopeSpans": [{
        "scope": {
          "name": "example"
        },
        "spans": [{
          "traceId": "0102030405060708090a0b0c0d0e0f10",
          "spanId": "0102030405060708",
          "name": "chat.completion",
          "startTimeUnixNano": "1700000000000000000",
          "endTimeUnixNano": "1700000001000000000",
          "attributes": [{
            "key": "gen_ai.system",
            "value": { "stringValue": "openai" }
          }, {
            "key": "gen_ai.response.model",
            "value": { "stringValue": "gpt-4o" }
          }, {
            "key": "gen_ai.usage.input_tokens",
            "value": { "intValue": "12" }
          }, {
            "key": "gen_ai.usage.output_tokens",
            "value": { "intValue": "8" }
          }, {
            "key": "gen_ai.input.messages",
            "value": {
              "stringValue": "[{\"role\":\"user\",\"content\":\"What is the weather today?\"}]"
            }
          }, {
            "key": "gen_ai.output.messages",
            "value": {
              "stringValue": "[{\"role\":\"assistant\",\"content\":\"It is sunny and warm.\"}]"
            }
          }]
        }]
      }]
    }]
  }'
```

The response is an OTLP `ExportTraceServiceResponse` in the same encoding as
the request. For protobuf, send a protobuf-encoded
`ExportTraceServiceRequest` body with
`Content-Type: application/x-protobuf`. Compression can be enabled for
protobuf by sending the encoded body with `Content-Encoding: gzip` (or
`deflate`). JSON exports should be sent uncompressed.

## OpenTelemetry Collector

Configure the Collector's `otlphttp` exporter with the Raindrop endpoint and
write key:

```yaml theme={null}
exporters:
  otlphttp/raindrop:
    endpoint: https://api.raindrop.ai
    encoding: proto
    headers:
      Authorization: "Bearer ${RAINDROP_WRITE_KEY}"
      X-Raindrop-Project-Id: "support-prod"
    compression: gzip

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: []
      exporters: [otlphttp/raindrop]
```

The standard OTLP/HTTP exporter appends `/v1/traces` to the endpoint. You can
also set the exporter endpoint explicitly to
`https://api.raindrop.ai/v1/traces`.

## Standard OTLP environment variables

The standard OTLP environment variables are the language-agnostic way to
configure an OTLP exporter:

```bash theme={null}
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://api.raindrop.ai/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer%20${RAINDROP_WRITE_KEY}"
```

`%20` encodes the space after `Bearer`, which `opentelemetry-python` and other
SDKs URL-decode when reading these variables.

## OpenTelemetry SDKs

### TypeScript

Use the OpenTelemetry OTLP/HTTP trace exporter:

```bash theme={null}
pnpm add @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http
```

```typescript theme={null}
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { NodeSDK } from "@opentelemetry/sdk-node";

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: "https://api.raindrop.ai/v1/traces",
    headers: {
      Authorization: `Bearer ${process.env.RAINDROP_WRITE_KEY}`,
    },
  }),
});

sdk.start();
```

For a short-lived process, call `await sdk.shutdown()` after creating spans so
the exporter flushes before exit.

### Python

Use the standard OTLP environment variables above with the Python OTLP/HTTP
exporter, keeping the `%20` after `Bearer`.

## Attributes Raindrop maps

These current [GenAI semantic conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/)
populate the corresponding span and interaction fields:

* `gen_ai.system` identifies the provider, and marks the span as an LLM call.
  Set it on every span you want treated as a generation.
* `gen_ai.response.model` populates the model.
* `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens` populate token
  usage. Send both, alongside `gen_ai.response.model`.
* `gen_ai.input.messages` and `gen_ai.output.messages` populate input and
  output messages. Use JSON arrays of message objects with `role` and
  `content` fields; content can be text or structured parts.
* `gen_ai.system_instructions` populates system instructions.

## Limits and troubleshooting

The default rate limit is 1,000 requests per 60 seconds per write key.
Organizations may have a different configured limit. When the endpoint returns
`429`, honor `Retry-After: 60` and retry with backoff. Batch spans through the
Collector or SDK exporter rather than sending one request per span.

The route treats `application/json` as OTLP/HTTP JSON and everything else,
including a missing content type, as protobuf. Always set `Content-Type`
explicitly.

`gzip` and `deflate` are the supported `Content-Encoding` values; any other
value, including `zstd`, returns `400`. A request body that exceeds 32 MiB once
decompressed returns `413`.
