Send data
OpenTelemetry
Send traces from any OpenTelemetry SDK or Collector over OTLP/HTTP, JSON or protobuf.
Anything that speaks OpenTelemetry can send traces to knotel: Node.js, Python, Go and Java services, and the OpenTelemetry Collector. Auto-instrumentation libraries give you database spans for MongoDB, Postgres, Redis and more without code changes.
Endpoint
| URL | https://YOUR-INSTANCE/v1/traces |
| Method | POST |
| Protocol | OTLP/HTTP, JSON or protobuf encoding |
| Auth | x-knotel-key: kn_…, or Authorization: Bearer kn_… |
| Body | Up to 5 MB and 5,000 spans per request, uncompressed |
Environment variables
SDKs that read the standard variables need only these:
OTEL_SERVICE_NAME=orders-api OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://YOUR-INSTANCE/v1/traces OTEL_EXPORTER_OTLP_TRACES_HEADERS=x-knotel-key=kn_YOUR_KEY
Node.js
@opentelemetry/exporter-trace-otlp-http sends JSON, so it works directly. With auto-instrumentations you get HTTP, Express, MongoDB, pg, Redis and other spans automatically.
// instrumentation.ts, loaded before your app (node --import ./instrumentation.js)
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
new NodeSDK({
serviceName: "orders-api",
traceExporter: new OTLPTraceExporter({
url: "https://YOUR-INSTANCE/v1/traces",
headers: { "x-knotel-key": process.env.KNOTEL_KEY },
}),
instrumentations: [getNodeAutoInstrumentations()],
}).start();Other languages
Python's and Go's HTTP exporters send protobuf, which ingest accepts directly — no gateway needed. For batching or sampling you can still put an OpenTelemetry Collector in front and have it forward to knotel. The Collector batches, and can sample or drop noisy spans before they cost you anything.
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
exporters:
otlphttp/knotel:
traces_endpoint: https://YOUR-INSTANCE/v1/traces
compression: none
headers:
x-knotel-key: kn_YOUR_KEY
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/knotel]Attributes knotel understands
Every attribute is stored and searchable. These are also indexed as columns for fast filtering and power the service map. Older semantic convention names are accepted too.
| Field | Read from |
|---|---|
| HTTP method | http.request.method, http.method |
| HTTP route | http.route |
| HTTP status | http.response.status_code, http.status_code |
| URL | url.full, http.url, url.path, http.target |
| Database system | db.system.name, db.system |
| Database operation | db.operation.name, db.operation |
| Database namespace | db.namespace, db.name |
| Collection / table | db.collection.name, db.mongodb.collection, db.sql.table |
| Query text | db.query.text, db.statement |
| Peer | peer.service, server.address, net.peer.name |
Sampling
knotel samples nothing itself: every span it receives is stored. When your SDK samples, tell knotel the rate and counts are scaled back up — a service sampling at 2% otherwise reads as fifty times quieter than it is. Error rates and percentiles survive sampling on their own; counts do not.
OpenTelemetry's consistent probability samplers write their rate into the W3C tracestate header as ot=th:<threshold>, and knotel reads it with no setup at all. That covers the Collector's probabilistic_sampler processor and the SDKs' consistent samplers, where the rate rides on every span of a sampled trace.
Plain TraceIdRatioBased is not one of them: it drops spans and records nothing about it. For that sampler, and for sampling you do yourself, set one of these as a span attribute, or on the resource for a whole service:
| Attribute | Meaning |
|---|---|
sampleRate | 1 in N kept: 50 means this span stands for fifty. sample_rate and SampleRate work too. |
sampling.probability | The fraction kept (0.02), i.e. the sampler's own argument. sampler.ratio works too. |
A span attribute wins over tracestate, which wins over a rate on the resource: each is more specific about this span than the next. Undeclared and unpropagated means 1: one span counts as one request. Nothing else is inferred, because guessing a rate would invent traffic that never happened. Where counts are scaled, the dashboard says how many spans are behind the estimate.
Responses
| Status | Meaning |
|---|---|
| 200 | Stored. If some spans were invalid, the body has partialSuccess.rejectedSpans. |
| 400 | The body isn't valid JSON or protobuf. |
| 401 | Missing or unknown ingest key. |
| 413 | The body is over 5 MB. Send smaller batches. |
| 415 | Not OTLP/HTTP. Send JSON or protobuf. |
| 503 | The spans couldn't be stored. Retry later. |
Ids may be hex or base64, kinds and status codes numbers or enum names, and timestamps nanoseconds as numbers or strings. Spans without a valid trace id, span id or start time are rejected. Long strings are trimmed (attributes at 8,000 characters, query text at 4,000). CORS is open, so browsers can post directly. See limits.