OpenTelemetry, Prometheus & Tracing
OpenTelemetry, Prometheus remote-write, Jaeger trace queries, and RUM web-vitals ingest for BugWatch.
Scope required for all write endpoints: ingest:write
Key facts:
- OTLP/HTTP (
/v1/logs,/v1/traces,/v1/metrics) accept bothapplication/jsonandapplication/x-protobuf. The OpenTelemetry Collector sends protobuf by default; the requests in this folder use JSON for readability. - An OTLP/gRPC receiver is also available on port
4317(logs, traces and metrics services); pass the same credential as gRPC metadata (x-api-keyorauthorization: Bearer ...). - The BugWatch JS and PHP SDKs export their own spans (
withSpan/startSpan) toPOST /v1/tracesnatively, so you do not need an OTel SDK or collector for BugWatch tracing. POST /api/v1/prom/writeaccepts Snappy-compressed Prometheus protobuf, which is always sent by the Prometheus remote-write adapter or thebugwatch-otel-collector. This folder documents the endpoint but does not include a fake binary body.- Prometheus query endpoints (
/query,/query_range,/label/__name__/values) are fully runnable;/queryand/query_rangealso accept POST with form-encoded parameters, which is what Grafana's Prometheus data source sends by default. - Jaeger GET endpoints (
/jaeger/api/services,/jaeger/api/traces,/jaeger/api/traces/{traceId}) are fully runnable. - RUM (
/api/v1/rum) acceptsx-bugwatch-session(browser) orx-api-key(server).
Auth header: x-api-key: {{apiKey}} or Authorization: Bearer {{apiKey}}
OTLP/HTTP – ingest logs (JSON)
Sends OpenTelemetry logs to BugWatch over OTLP/HTTP.
Scope: ingest:write
Send application/json, as in this example, or application/x-protobuf with a protobuf ExportLogsServiceRequest, which is what the OTel Collector sends. A fully accepted batch answers with an empty {} object.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"resourceLogs": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeLogs": [
{
"logRecords": [
{
"timeUnixNano": "{{nowNano}}",
"severityNumber": 9,
"severityText": "INFO",
"body": {
"stringValue": "User signed in"
},
"attributes": [
{
"key": "user.id",
"value": {
"stringValue": "usr_123"
}
}
]
}
]
}
]
}
]
}Responses
200All accepted401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/v1/logs' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"resourceLogs": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeLogs": [
{
"logRecords": [
{
"timeUnixNano": "{{nowNano}}",
"severityNumber": 9,
"severityText": "INFO",
"body": {
"stringValue": "User signed in"
},
"attributes": [
{
"key": "user.id",
"value": {
"stringValue": "usr_123"
}
}
]
}
]
}
]
}
]
}'nullOTLP/HTTP – ingest traces (JSON)
Sends OpenTelemetry traces to BugWatch over OTLP/HTTP.
Scope: ingest:write
Send application/json, as in this example, or application/x-protobuf with a protobuf ExportTraceServiceRequest, which is what the OTel Collector sends. A fully accepted batch answers with an empty {} object.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeSpans": [
{
"spans": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "HTTP GET /api/users",
"kind": 2,
"startTimeUnixNano": "{{nowNano}}",
"endTimeUnixNano": "{{nowNano}}",
"status": {
"code": 1
},
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
},
{
"key": "http.status_code",
"value": {
"intValue": 200
}
}
]
}
]
}
]
}
]
}Responses
200All accepted401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/v1/traces' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeSpans": [
{
"spans": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "HTTP GET /api/users",
"kind": 2,
"startTimeUnixNano": "{{nowNano}}",
"endTimeUnixNano": "{{nowNano}}",
"status": {
"code": 1
},
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
},
{
"key": "http.status_code",
"value": {
"intValue": 200
}
}
]
}
]
}
]
}
]
}'nullOTLP/HTTP – ingest metrics (JSON)
Sends OpenTelemetry metrics to BugWatch over OTLP/HTTP.
Scope: ingest:write
Send application/json, as in this example, or application/x-protobuf with a protobuf ExportMetricsServiceRequest, which is what the OTel Collector sends. A fully accepted batch answers with an empty {} object.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"resourceMetrics": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeMetrics": [
{
"metrics": [
{
"name": "http.server.request.duration",
"unit": "ms",
"gauge": {
"dataPoints": [
{
"timeUnixNano": "{{nowNano}}",
"asDouble": 123.4,
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
}
]
}
]
}
}
]
}
]
}
]
}Responses
200All accepted401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/v1/metrics' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"resourceMetrics": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeMetrics": [
{
"metrics": [
{
"name": "http.server.request.duration",
"unit": "ms",
"gauge": {
"dataPoints": [
{
"timeUnixNano": "{{nowNano}}",
"asDouble": 123.4,
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
}
]
}
]
}
}
]
}
]
}
]
}'nullPrometheus remote-write (DOCUMENTATION ONLY)
Receives Prometheus remote-write samples.
Scope: ingest:write
To use Prometheus remote-write with BugWatch:
- Deploy the
bugwatch-otel-collector(or configure Prometheusremote_write). - Set the remote-write URL to
{{baseUrl}}/api/v1/prom/write. - Add the header
x-api-key: sk_live_KEYID:secret.
Authorization
x-api-keyAPI key · headerrequiredHeaders
X-Prometheus-Remote-Write-VersionRequest body
text/plain<<< BINARY SNAPPY-COMPRESSED PROTOBUF - sent by Prometheus/collector, not a manual JSON request >>>Responses
204Samples accepted400Malformed protobuf401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/api/v1/prom/write' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: text/plain' \
--data-raw '<<< BINARY SNAPPY-COMPRESSED PROTOBUF - sent by Prometheus/collector, not a manual JSON request >>>'nullPrometheus query (instant)
Returns the latest value of each matching metric series at one point in time, using a PromQL-compatible selector.
Scope: ingest:write (the key you write with is accepted for queries)
The same query also works as a POST with form-encoded parameters, which is what Grafana's Prometheus data source sends.
Authorization
x-api-keyAPI key · headerrequiredParameters
queryquery · stringdefault: Required. Metric name or label selector, such as http_requests_total or {service="api-gateway"}.
timequery · stringdefault: Optional. Evaluation time in Unix seconds (decimals allowed). Defaults to now.
Responses
200Instant vector401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/prom/query?query=http_requests_total&time=1767225600' \
-H 'x-api-key: YOUR_API_KEY'{
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": {
"__name__": "http_requests_total",
"service": "api"
},
"value": [
1767225600,
"42"
]
}
]
}
}Prometheus query_range (over time)
Returns every stored point of each matching metric series over a time range.
Scope: ingest:write
The same query also works as a POST with form-encoded parameters, which is what Grafana's Prometheus data source sends.
Authorization
x-api-keyAPI key · headerrequiredParameters
queryquery · stringdefault: Required. Metric name or label selector, such as http_requests_total or {service="api-gateway"}.
startquery · stringdefault: Range start in Unix seconds (decimals allowed). Defaults to 0, the oldest stored point.
endquery · stringdefault: Range end in Unix seconds (decimals allowed). Defaults to now.
Responses
200Matrix result401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/prom/query_range?query=http_requests_total&start=1767222000&end=1767225600' \
-H 'x-api-key: YOUR_API_KEY'{
"status": "success",
"data": {
"resultType": "matrix",
"result": [
{
"metric": {
"__name__": "http_requests_total"
},
"values": [
[
1767222000,
"38"
],
[
1767225600,
"42"
]
]
}
]
}
}Prometheus label __name__ values (list metric names)
Lists every distinct metric name stored for the project and environment your key is scoped to.
Scope: ingest:write
Authorization
x-api-keyAPI key · headerrequiredResponses
200Metric names401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/prom/label/__name__/values' \
-H 'x-api-key: YOUR_API_KEY'{
"status": "success",
"data": [
"http_requests_total",
"rum.lcp",
"rum.cls",
"rum.inp"
]
}Jaeger – list service names
Lists the distinct service names that have sent traces to this BugWatch project.
Scope: ingest:write (the same API key)
Authorization
x-api-keyAPI key · headerrequiredResponses
200Services401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/jaeger/api/services' \
-H 'x-api-key: YOUR_API_KEY'{
"data": [
"api-gateway",
"payment-service",
"user-service"
],
"total": 3,
"limit": 3,
"offset": 0,
"errors": null
}Jaeger – list traces
Lists recent distributed traces for the project and environment your key is scoped to, newest first, each with its spans in the Jaeger format.
Scope: ingest:write
Authorization
x-api-keyAPI key · headerrequiredParameters
limitquery · stringdefault: Optional. Maximum number of traces, up to 100. Defaults to 20.
lookbackquery · stringdefault: Optional. How far back to look: 1h, 24h, 7d or 30d. Defaults to 1h; any other value is treated as 1h.
Responses
200Traces list401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/jaeger/api/traces?limit=20&lookback=1h' \
-H 'x-api-key: YOUR_API_KEY'{
"data": [],
"total": 0,
"limit": 20,
"offset": 0,
"errors": null
}Jaeger – get one trace
Returns every span of one trace in the Jaeger format.
Scope: ingest:write
traceId in the path is the 32-character hex trace identifier, as sent in traceparent or returned by Jaeger – list traces. A trace id with no stored spans answers 200 with an empty data list, not 404.
Authorization
x-api-keyAPI key · headerrequiredParameters
traceIdpath · stringrequireddefault: Responses
200Trace401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/jaeger/api/traces/{traceId}' \
-H 'x-api-key: YOUR_API_KEY'{
"data": [
{
"traceID": "4bf92f3577b34da6a3ce929d0e0e4736",
"spans": []
}
],
"total": 1,
"limit": 1,
"offset": 0,
"errors": null
}RUM – ingest web-vitals
Ingests Real User Monitoring (RUM) web-vitals metrics (LCP, CLS, INP, FID, TTFB).
Authenticate with x-bugwatch-session from a browser, or with x-api-key from a server or while testing.
Scope (for x-api-key): ingest:write
Request body: three shapes are accepted.
- Wrapped array:
{ "vitals": [{ "name": "LCP", "value": 1250.5, "url": "https://example.com/", "rating": "good" }] }- Bare array:
[{ "name": "CLS", "value": 0.05, "rating": "good" }, { "name": "INP", "value": 180, "rating": "needs-improvement" }]- Single vital object:
{ "name": "TTFB", "value": 320, "url": "https://example.com/checkout", "rating": "needs-improvement" }Vital field schema:
| Field | Type | Notes |
|---|---|---|
name | string (required) | LCP, CLS, INP, FID, TTFB |
value | number (required) | Metric value in ms (or unitless for CLS) |
url | string (optional) | Page URL |
rating | string (optional) | good, needs-improvement or poor |
id | string (optional) | Client-side unique id |
Vitals are stored as rum.<name_lower> metric points (for example rum.lcp, rum.cls). A browser call from an origin outside the project's allowed origins is refused with 401, the same as a bad session token.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"vitals": [
{
"name": "LCP",
"value": 1250.5,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "CLS",
"value": 0.05,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "INP",
"value": 180,
"url": "https://example.com/",
"rating": "needs-improvement"
}
]
}Responses
202Vitals ingested400Malformed JSON401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/api/v1/rum' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"vitals": [
{
"name": "LCP",
"value": 1250.5,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "CLS",
"value": 0.05,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "INP",
"value": 180,
"url": "https://example.com/",
"rating": "needs-improvement"
}
]
}'{
"ingested": 3
}