Skip to content

Observability

Preview The observability resource exposes the same insights you see in the dashboard — summaries, timeseries, page breakdowns, logs, traces, resource metrics, and the per-request ledger — as read-only API endpoints. Most reads are environment-scoped (an env carries its own telemetry identity); a handful of rollups are account-scoped. Everything here is gated by a single scope, observability:read, and everything respects the same PII hard-cut and per-site sampling rules the UI does.

For the data model behind these reads — what a trace, a request row, or web-vitals mean — start with observability overview. This page is the API reference for fetching that data programmatically.

All observability reads require the observability:read scope. write is not a thing here — observability is read-only over the API.

observability:read

Tenancy is enforced two ways depending on the route:

  • Env-scoped reads load the environment first, so an out-of-scope env returns 404 (existence hiding) and a key pinned to a different env never sees it. See errors for the 404-vs-403 split.
  • Account-scoped reads (/v1/usage/storage, /v1/logs, /v1/traces, /v1/metrics) are loaderless — there is no resource in the path to load, so tenancy is resolved inside the handler from your principal. A key’s resource constraint is honored here too: if the key is pinned to a site or team, these rollups are intersected down to only what that key may see.

All of these live under an environment and return the response envelope. Collections are cursor-paginated — pass ?limit= and ?cursor=, read pagination.next_cursor and pagination.has_more back.

Method + path Returns
GET …/{envID}/insights/summary rolled-up totals + p95 latency for a window
GET …/{envID}/insights/timeseries a metric bucketed over time
GET …/{envID}/insights/pages top paths by volume, errors, or latency
GET …/{envID}/insights/logs application log lines
GET …/{envID}/insights/traces request traces (sampled per site/plan)
GET …/{envID}/insights/traces/{id} one trace, span by span
GET …/{envID}/insights/resources CPU / memory / I/O resource metrics
GET …/{envID}/insights/requests the per-request ledger (blocks excluded)

The full path prefix is /v1/sites/{siteID}/environments/{envID}. Blocked requests are kept out of insights/requests — they live under the security resource, exactly as the UI separates Requests from Security → Blocks.

Two of these views stream. They hold the connection open and push rows as they happen, over server-sent events:

Method + path Streams
GET …/{envID}/insights/logs/stream log lines, live
GET …/{envID}/insights/requests/stream served requests, live
Name Type Required Description
from string (RFC 3339) no start of the window; defaults to the env’s retention floor
to string (RFC 3339) no end of the window; defaults to now
interval string timeseries bucket size, e.g. 1m, 5m, 1h
metric string timeseries which series, e.g. requests, p95_latency_ms, error_rate
status string no filter requests/traces to a status class, e.g. 5xx
limit integer no page size for collections
cursor string no opaque pagination cursor

These roll telemetry up across everything your key can see, with no env in the path. They are loaderless: tenancy is resolved in-handler and the key’s resource constraint is intersected in, so a site-pinned key gets a site-pinned rollup.

Method + path Returns
GET /v1/usage/storage storage usage across owned sites/envs
GET /v1/logs account-wide log search
GET /v1/traces account-wide trace search
GET /v1/metrics account-wide metric rollups

Fetch p95 latency for a production environment, bucketed by five minutes.

GET a p95 timeseries
curl https://api.managed.dev/v1/sites/site_01J7Q2/environments/env_01J8D9/insights/timeseries \
--get \
--data-urlencode "metric=p95_latency_ms" \
--data-urlencode "interval=5m" \
--data-urlencode "from=2026-06-24T00:00:00Z" \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23"
Response
{
"data": {
"metric": "p95_latency_ms",
"interval": "5m",
"points": [
{ "t": "2026-06-24T00:00:00Z", "value": 142 },
{ "t": "2026-06-24T00:05:00Z", "value": 138 },
{ "t": "2026-06-24T00:10:00Z", "value": 201 }
]
},
"request_id": "req_01J9AB"
}

Hold a connection open and read application logs as they’re written. The endpoint streams server-sent events; each data: frame is one log line in the envelope’s data shape.

Tail an environment's logs
curl -N https://api.managed.dev/v1/sites/site_01J7Q2/environments/env_01J8D9/insights/logs/stream \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23" \
-H "Accept: text/event-stream"
Stream frames
event: log
data: {"ts":"2026-06-24T00:11:03.221Z","level":"error","path":"/checkout/","msg":"db timeout after 5000ms"}
event: log
data: {"ts":"2026-06-24T00:11:03.998Z","level":"info","path":"/checkout/","msg":"retry succeeded"}

The requests/stream endpoint works identically — point it at a deploy window filtered to ?status=5xx to watch for regressions in real time, then roll back if errors scroll in.