# RightSignals > RightSignals is an OpenTelemetry-native error and signal tracker. It ingests > standard OTLP over HTTP. There is no proprietary SDK — not for Ruby, Crystal, > or any other language. The "SDK" is the official OpenTelemetry library for your > language. If you can export OTLP, you can report to RightSignals in minutes. ## The one thing to know Do not look for a proprietary RightSignals SDK, a Crystal SDK, or a private Ruby gem. None exists and none is needed. RightSignals is just an OTLP endpoint. Point any OpenTelemetry exporter at it with a bearer token and you are done. ## Key model - **Reporter key** (`rsp_…`): per-project, publishable. Used to authenticate OTLP ingest for a specific project. Find it on the project's settings page at `/projects/:id`. Safe to commit in CI config or expose in a collector; if it leaks, the risk is spammy telemetry only. - **Org API key** (`rsg_…`): per-org, private. Used to call the read/management REST API at `/api/v1/*`. Find it on `/organization`. Treat it like a secret. One project per codebase/app. Use `deployment.environment.name=staging|production` to separate stages within the same project — do NOT create separate projects or suffix `service.name` with the environment. ## Ingestion API - Base URL: `https://app.rightsignals.com` - Traces: `POST /v1/traces` - Logs: `POST /v1/logs` - Protocol: OpenTelemetry OTLP over HTTP (HTTP only; no gRPC yet) - Content-Type: `application/json`, `application/protobuf`, or `application/x-protobuf` - Compression: optional `Content-Encoding: gzip` - Auth: `Authorization: Bearer ` - Error responses: `google.rpc.Status` JSON - Metrics (`/v1/metrics`) are not accepted yet. ## Concepts: Trace, Occurrence, Issue Three core nouns (OTLP mapping in parentheses): - **Trace** — one request's journey: a tree of spans with timing/status; every request produces one, error or not (OTLP traces/spans). Powers performance/APM. - **Occurrence** — one error instance, captured from a span's `exception` event (error-log capture is planned). Carries the stack trace and **both** a `trace_id` and a `fingerprint` (see below). RightSignals' projection of an error; Sentry/Bugsnag call it an "event" — we use "occurrence" to avoid colliding with OTLP events/logs. - **Issue** — the defect: all occurrences sharing a `fingerprint`, with status/first-seen/last-seen/count/regression markers. What you triage and resolve. **Two different keys, two different questions** (this trips people up): `trace_id` = *which request* (shared by every span in one request); `fingerprint` = *which bug* (a hash of exception type + normalized stack frames, shared across requests). A trace has no fingerprint; an occurrence has both. Example — 6 real occurrences of one `RuntimeError`, fingerprint `285ce59a7319…`, all in Issue #3, but six *different* `trace_id`s (`c07dec74…`, `85189fb9…`, `b9ff9642…`, …): same fingerprint groups them into one issue; the differing trace_ids mean they were six separate requests. `trace_id` never groups occurrences — `fingerprint` does. Hierarchy: Trace → Spans → (a span's exception) → Occurrence → grouped by fingerprint → Issue. Supporting nouns: **Span** (one operation in a trace), **Log/Event** (an OTLP log record; an "event" is a log record with an `event.name`), **Release** (a deployed `service.version`), **Environment** (`deployment.environment.name`, a dimension across every signal). Issues are grouped from the OTel exception semantic convention: an `exception` event on a span carrying `exception.type`, `exception.message`, and `exception.stacktrace`. Every official OpenTelemetry SDK emits this — nothing RightSignals-specific is required. ## Agent self-serve setup (with org API key in env) Create or retrieve a project (idempotent by slug; 201 new / 200 existing): curl -X POST https://app.rightsignals.com/api/v1/projects \ -H "Authorization: Bearer $RIGHTSIGNALS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"project":{"name":"my-app"}}' Response includes `project.reporter_key`, `otlp.endpoint`, `otlp.headers`, and `otlp.recommended_resource_attributes`. Use those values to configure the exporter. Also: `GET /api/v1/projects` (list) and `GET /api/v1/projects/:slug` (show). Per-org project cap: `PROJECT_LIMIT_PER_ORG` (default 10); over-limit → 422 `project_limit_reached`. ## Ruby on Rails (~15 minutes) Gemfile: gem "opentelemetry-sdk" gem "opentelemetry-exporter-otlp" gem "opentelemetry-instrumentation-all" config/initializers/opentelemetry.rb: require "opentelemetry/sdk" require "opentelemetry/instrumentation/all" require "opentelemetry-exporter-otlp" OpenTelemetry::SDK.configure do |c| c.service_name = ENV.fetch("OTEL_SERVICE_NAME", "your-app") c.resource = OpenTelemetry::SDK::Resources::Resource.create( "service.version" => ENV.fetch("HEROKU_SLUG_COMMIT", "unknown"), "deployment.environment.name" => ENV.fetch("RAILS_ENV", "production") ) c.use_all end Environment: OTEL_EXPORTER_OTLP_ENDPOINT=https://app.rightsignals.com OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_SERVICE_NAME=your-app (Use a literal space after `Bearer` — do not URL-encode it as `%20`. Not all OTel SDKs percent-decode `OTEL_EXPORTER_OTLP_HEADERS`, so a literal space is the portable form.) (`service.version` is set in code from `HEROKU_SLUG_COMMIT` so it stays current on each deploy. Do not set it via `OTEL_RESOURCE_ATTRIBUTES` — that env var does not expand `$VARS`, so it would be frozen at the literal string, not the commit SHA.) Restart the app. Unhandled exceptions in controllers and background jobs are now captured automatically. ## Smoke test curl -i "https://app.rightsignals.com/v1/traces" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $RIGHTSIGNALS_REPORTER_KEY" \ --data @trace.json A 200 response with an `X-RightSignals-Occurrences-Captured` header means it worked. See /llms-full.txt for a ready-to-send `trace.json`. ## More - Full integration guide for agents: https://app.rightsignals.com/llms-full.txt - Human docs: https://app.rightsignals.com/docs - OpenAPI / Swagger: https://app.rightsignals.com/api-docs