# OpenTelemetry (OTLP)

Grepcat ingests OpenTelemetry data over **OTLP/HTTP**. Point any
OpenTelemetry SDK or Collector at the endpoint below and your traces, logs, and
metrics flow into the same projects as your Sentry data.

## Endpoint

Use your deployment's base URL as the OTLP endpoint. The standard OTLP/HTTP
signal paths are appended automatically by exporters:

| Signal  | Path          |
| ------- | ------------- |
| Traces  | `/v1/traces`  |
| Logs    | `/v1/logs`    |
| Metrics | `/v1/metrics` |

Set the **base** endpoint and the exporter will append the per-signal path:

```bash
OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-deployment-host>
```

If you need to target a single signal, set the full path with the signal-specific
variable, e.g.:

```bash
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<your-deployment-host>/v1/traces
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://<your-deployment-host>/v1/logs
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://<your-deployment-host>/v1/metrics
```

## Authentication

The **DSN public key** is the credential — it selects the project (there is no
project id in the OTLP path). It is the random token in your Sentry DSN:

```
https://<DSN_PUBLIC_KEY>@<host>/<project-id>
            ^^^^^^^^^^^^^^ this is the credential
```

The preferred way to authenticate is a Bearer token via OTLP headers:

```bash
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer%20<DSN_PUBLIC_KEY>
```

> Note: `OTEL_EXPORTER_OTLP_HEADERS` is URL-encoded. `%20` is the space between
> `Bearer` and the key. You may also pass a full DSN as the Bearer token; the
> public key is extracted from it.

Two other forms are accepted if Bearer headers are inconvenient:

```bash
# Header form
OTEL_EXPORTER_OTLP_HEADERS=X-Sentry-Key=<DSN_PUBLIC_KEY>

# Query-string form (full DSN), appended to the endpoint
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://<your-deployment-host>/v1/traces?dsn=<FULL_DSN>
```

## Protocol & encoding

```bash
# Default and recommended. Binary protobuf over HTTP.
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

# Also supported: OTLP/JSON over HTTP.
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
```

- **`http/protobuf`** — the SDK default. Supported.
- **`http/json`** — supported.
- **gzip** request compression is supported (`Content-Encoding: gzip`). Most
  exporters enable it with `OTEL_EXPORTER_OTLP_COMPRESSION=gzip`.

Request bodies are capped at 8 MB after decompression.

## Copy-paste env block

```bash
export OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-deployment-host>
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer%20<DSN_PUBLIC_KEY>
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
```

That single block sends traces, logs, and metrics to the correct paths with the
right auth and encoding.

## gRPC is NOT supported

**OTLP/gRPC (`grpc://`, the `grpc` protocol) is not supported.** The backend
runs on Cloudflare Workers, which cannot act as a gRPC server — gRPC depends on
HTTP/2 response trailers, which Workers do not emit. Only **OTLP/HTTP** works.

You have two options:

### Option A — switch your SDK to HTTP

Set the protocol to `http/protobuf` (or `http/json`) and use the HTTP endpoint.
This is the simplest fix and requires no extra infrastructure:

```bash
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-deployment-host>
export OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer%20<DSN_PUBLIC_KEY>
```

### Option B — run an OpenTelemetry Collector

If you must keep gRPC on the client side (e.g. an existing fleet that only
speaks OTLP/gRPC), run an **OpenTelemetry Collector** that receives gRPC and
re-exports over OTLP/HTTP to this endpoint using the `otlphttp` exporter:

```yaml
# otelcol-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317   # your apps send gRPC here
      http:
        endpoint: 0.0.0.0:4318

exporters:
  otlphttp:
    endpoint: https://<your-deployment-host>
    encoding: proto              # http/protobuf; use "json" for OTLP/JSON
    compression: gzip
    headers:
      Authorization: "Bearer <DSN_PUBLIC_KEY>"

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlphttp]
    logs:
      receivers: [otlp]
      exporters: [otlphttp]
    metrics:
      receivers: [otlp]
      exporters: [otlphttp]
```

The `otlphttp` exporter appends `/v1/traces`, `/v1/logs`, and `/v1/metrics` to
the `endpoint` for you. Note the Collector `headers` value is a literal string
(`Bearer <key>`, with a real space) — no URL-encoding, unlike the SDK
`OTEL_EXPORTER_OTLP_HEADERS` env var.

## Responses & errors

| Status | Meaning                                                  |
| ------ | -------------------------------------------------------- |
| `200`  | Accepted (empty OTLP success body in the request encoding) |
| `400`  | Malformed body (invalid protobuf / JSON / encoding)     |
| `401`  | Missing, unknown, or revoked DSN public key             |
| `413`  | Payload too large (> 8 MB decompressed)                 |
| `415`  | Unsupported `Content-Type` (use protobuf or JSON)       |
| `429`  | Rate limited (see `Retry-After`)                        |
