Telemetry Read API
The Telemetry Read API returns normalized OpenTelemetry logs and metric points for a project. Use the Trace Read API for trace, span, conversation, and observed-user views. Use the
Base URL:
https://api.provon.dev/v1Public service clients use a project API key with telemetry:read:
Authorization: Bearer <PROVON_API_KEY>The key selects the project, so public routes do not require a project ID in the path.
Endpoints#
| Method | Public API path | Workbench path | Purpose |
|---|---|---|---|
GET |
/logs |
/projects/:projectId/logs |
List logs |
GET |
/metrics |
/projects/:projectId/metrics |
List metrics |
The project-qualified forms are for signed-in Workbench calls and project-scoped clients that need
an explicit path. API integrations should normally use /logs and /metrics.
Time Range#
Every request must provide one time-range form:
| Form | Example |
|---|---|
| Relative range | range=15m, range=6h, or range=7d |
| Absolute range | startMs=1760000000000&endMs=1760086400000 |
| Encoded custom range | range=1760000000000-1760086400000 |
Times are Unix milliseconds. When using startMs/endMs, both values are required and startMs
must not exceed endMs.
List Logs#
GET /v1/logs?range=1h&limit=100
Authorization: Bearer <PROVON_API_KEY>Query parameters:
| Parameter | Type | Purpose |
|---|---|---|
range |
string | Relative or encoded absolute range |
startMs |
Unix millisecond | Absolute range start |
endMs |
Unix millisecond | Absolute range end |
traceId |
string | Return logs correlated to one trace |
eventName |
string | Return logs with one normalized event name |
limit |
integer | Page size from 1 to 500; default 100 |
cursor |
string | Opaque cursor from the previous response |
Response:
{
"logs": [
{
"trace_id": "4f3c2f...",
"span_id": "9a41...",
"time_ms": 1760086000000,
"observed_time_ms": 1760086000042,
"severity_text": "INFO",
"body": {
"message": "tool call completed"
},
"attributes": {
"tool.name": "lookup_order"
},
"resource_attributes": {
"service.name": "support-agent"
},
"scope_name": "agent-runtime",
"event_name": "tool.result"
}
],
"pagination": {
"limit": 100,
"nextCursor": null
}
}body and attribute values are JSON values. trace_id, span_id, timestamps, severity, scope,
and event name can be null when the source did not provide them.
List Metrics#
GET /v1/metrics?range=24h&metricNames=gen_ai.client.token.usage,gen_ai.client.operation.duration
Authorization: Bearer <PROVON_API_KEY>Query parameters:
| Parameter | Type | Purpose |
|---|---|---|
range |
string | Relative or encoded absolute range |
startMs |
Unix millisecond | Absolute range start |
endMs |
Unix millisecond | Absolute range end |
metricNames |
CSV string | Exact metric names to include |
limit |
integer | Page size from 1 to 500; default 100 |
cursor |
string | Opaque cursor from the previous response |
Response:
{
"metrics": [
{
"metric_name": "gen_ai.client.operation.duration",
"description": "Model operation duration",
"unit": "s",
"type": "histogram",
"start_time_ms": 1760085000000,
"time_ms": 1760086000000,
"attributes": {
"gen_ai.operation.name": "chat",
"gen_ai.provider.name": "openai"
},
"resource_attributes": {
"service.name": "support-agent"
},
"scope_name": "agent-runtime",
"value": null,
"count": 24,
"sum": 31.2,
"min": 0.4,
"max": 4.8,
"bucket_counts": [3, 12, 8, 1],
"explicit_bounds": [0.5, 1, 2],
"exemplars": null
}
],
"pagination": {
"limit": 100,
"nextCursor": null
}
}Metric type is one of gauge, sum, histogram, exponential_histogram, or summary.
Fields that do not apply to that type are null.
Pagination#
Results use cursor pagination in descending telemetry time order. Pass pagination.nextCursor
unchanged:
curl --get "$PROVON_API_URL/logs" \
-H "Authorization: Bearer $PROVON_API_KEY" \
--data-urlencode "range=24h" \
--data-urlencode "cursor=$NEXT_CURSOR"Treat the cursor as opaque. Its encoding can change independently of record fields.
Read Consistency#
OTLP ingestion is asynchronous. A successful POST /v1/logs or POST /v1/metrics on the OTLP
surface means the payload was accepted for processing; it may not be immediately visible through
this API.
For verification:
- keep the original signal timestamp inside the requested range;
- poll with bounded backoff;
- narrow by
traceId,eventName, ormetricNameswhere possible; - stop retrying on authentication or validation errors.
Errors#
| Status | Meaning |
|---|---|
400 |
Missing/invalid time range or malformed cursor |
401 |
Missing or invalid project API key |
403 |
API key does not include telemetry:read |
500 |
Query or storage failure |
Authentication failures use the shared structured API error. Some query-validation failures return
a compact { "error": "..." } body; clients should use the HTTP status as the primary control
signal.