Metrics API
Table of Contents
upsquad/metrics/v1/metrics.proto
ApproverStat
ApproverStat is one row of the top-approvers leaderboard.
| Field | Type | Label | Description |
|---|---|---|---|
| member_id | string | member_id is the approver's members.id (UUID as string). May be empty when the approver was a system actor or pre-Wave-D record. | |
| name | string | name is the human-readable approver name (members.full_name) or the resolved_by string when no member row matches. | |
| count | int64 | count is decisions made by this approver within the window. | |
| p50_latency_seconds | double | p50_latency_seconds is the median (resolved_at - requested_at) for the approver's decisions in the window. |
FailureBreakdownRow
FailureBreakdownRow is one row of the failures breakdown table. Maps to metrics.jsx lines 175-184 (Failure / Count / Rate / Last seen / Δ).
| Field | Type | Label | Description |
|---|---|---|---|
| name | string | name is the human-readable workflow name (workflows.name) — we group failures by workflow rather than by error string so the table aligns with the "owning workflow" mental model the prototype uses. | |
| failures | int64 | failures is the count of workflow_runs.status='failed'. | |
| total_runs | int64 | total_runs is the count of workflow_runs (any status) for the same workflow over the window — the denominator for rate. | |
| rate | double | rate is failures/total_runs as a percentage (0..100). | |
| trend | double | trend is period-over-period delta on failures, as a signed %. | |
| root_cause | string | root_cause is best-effort: the most-recent run's context->>'error' (or 'root_cause') for this workflow's failures. Empty string when with_root_cause is false or no JSONB key found. | |
| last_seen_at | google.protobuf.Timestamp | last_seen_at is the most-recent created_at among failed runs. |
GetApprovalMetricsRequest
GetApprovalMetricsRequest filters the approvals throughput card. range_start / range_end bound the window over which p50/p95 latency and the top-approvers leaderboard are computed; pending counts are snapshot-only (the queue at query time) and are not windowed — matches the prototype card semantics.
| Field | Type | Label | Description |
|---|---|---|---|
| scope | MetricsScope | scope filters the aggregation. Same semantics as GetWorkflowsBreakdownRequest. | |
| scope_id | string | scope_id is the UUID of the org_unit when scope is pillar/team. | |
| range_start | google.protobuf.Timestamp | range_start defines the latency window start; defaults to range_end - 30d. | |
| range_end | google.protobuf.Timestamp | range_end defines the latency window end; defaults to server now. |
GetApprovalMetricsResponse
GetApprovalMetricsResponse contains the approvals card payload.
| Field | Type | Label | Description |
|---|---|---|---|
| pending_total | int64 | pending_total is the snapshot count of governance_approvals rows whose status='pending' (not windowed). | |
| pending_by_age | PendingByAgeBucket | repeated | pending_by_age is a 4-bucket histogram of pending rows by age (now - requested_at). Always 4 entries, ordered youngest-first. |
| approved_today | int64 | approved_today is the count of approvals whose resolved_at >= today's UTC midnight AND status='approved'. Independent of the [range_start, range_end) latency window so the "today" tiles stay anchored to wall-clock today. | |
| rejected_today | int64 | rejected_today is the same but for status='denied' resolutions. | |
| p50_latency_seconds | double | p50_latency_seconds is the median (resolved_at - requested_at) across resolved approvals within [range_start, range_end). 0 when no resolved rows fall in the window. | |
| p95_latency_seconds | double | p95_latency_seconds is the 95th percentile of the same series. | |
| top_approvers | ApproverStat | repeated | top_approvers is the leaderboard for the window, ordered by count desc with a hard cap of 10 rows. |
| range_start | google.protobuf.Timestamp | range_start echoes the resolved latency window start. | |
| range_end | google.protobuf.Timestamp | range_end echoes the resolved latency window end. | |
| generated_at | google.protobuf.Timestamp | generated_at is the server wall-clock at query time (so the frontend can label "today's" counters relative to a known anchor even when a timezone is involved on the rendering side). |
GetFailuresBreakdownRequest
GetFailuresBreakdownRequest filters the failures breakdown.
| Field | Type | Label | Description |
|---|---|---|---|
| scope | MetricsScope | scope filters the aggregation. Same semantics as GetWorkflowsBreakdownRequest. | |
| scope_id | string | scope_id is the UUID of the org_unit when scope is pillar/team. | |
| range_start | google.protobuf.Timestamp | range_start defaults to range_end - 30d. | |
| range_end | google.protobuf.Timestamp | range_end defaults to server wall-clock now. | |
| top_n | int32 | top_n caps the returned rows (default 20, max 100, clamped not rejected). | |
| with_root_cause | bool | with_root_cause asks the server to populate the root_cause field (best-effort from workflow_runs.context). False keeps the row lighter for callers that only want the count surface. |
GetFailuresBreakdownResponse
GetFailuresBreakdownResponse contains the ordered top-N rows.
| Field | Type | Label | Description |
|---|---|---|---|
| rows | FailureBreakdownRow | repeated | rows is ordered by failures descending, ties broken by name asc. |
| range_start | google.protobuf.Timestamp | range_start echoes the resolved window start. | |
| range_end | google.protobuf.Timestamp | range_end echoes the resolved window end. |
GetGuardrailsBreakdownRequest
GetGuardrailsBreakdownRequest filters the guardrails breakdown.
| Field | Type | Label | Description |
|---|---|---|---|
| scope | MetricsScope | scope filters the aggregation. Same semantics as GetWorkflowsBreakdownRequest. | |
| scope_id | string | scope_id is the UUID of the org_unit when scope is pillar/team. | |
| range_start | google.protobuf.Timestamp | range_start defaults to range_end - 30d. | |
| range_end | google.protobuf.Timestamp | range_end defaults to server wall-clock now. | |
| top_n | int32 | top_n caps the returned rows (default 20, max 100, clamped not rejected). |
GetGuardrailsBreakdownResponse
GetGuardrailsBreakdownResponse contains the ordered top-N rows.
| Field | Type | Label | Description |
|---|---|---|---|
| rows | GuardrailBreakdownRow | repeated | rows is ordered by fires descending, ties broken by name asc. |
| range_start | google.protobuf.Timestamp | range_start echoes the resolved window start. | |
| range_end | google.protobuf.Timestamp | range_end echoes the resolved window end. |
GetKPIsRequest
GetKPIsRequest selects the time window and hierarchical scope. All fields are optional. When range_start / range_end are unset the service defaults to the trailing 30-day window ending at the server's wall-clock now (UTC). When scope is METRICS_SCOPE_ORG (or unspecified) the scope_id field is ignored and the caller's org_id from the ambient JWT scope is used.
Tenant identity flows from the JWT-derived scope claims (org_id) on the gRPC metadata; never from the request payload.
| Field | Type | Label | Description |
|---|---|---|---|
| scope | MetricsScope | scope filters the aggregation to org / pillar / team. When unspecified or METRICS_SCOPE_ORG, the caller's org is used and scope_id is ignored. | |
| scope_id | string | scope_id is the UUID of the org_unit (pillar or team) when scope is METRICS_SCOPE_PILLAR / METRICS_SCOPE_TEAM. Ignored when scope is METRICS_SCOPE_ORG. The pillar/team filter is wired in Metrics 5/8 (#1116) via GetPillarSpend joins; the v1 GetKPIs RPC accepts the value but applies it only on the spend / workflow_runs paths (the rest of the tiles are tenant-wide reads gated by RLS). | |
| range_start | google.protobuf.Timestamp | range_start is the inclusive start of the query window (UTC). When unset, defaults to range_end - 30 days. | |
| range_end | google.protobuf.Timestamp | range_end is the exclusive end of the query window (UTC). When unset, defaults to the server's current wall-clock. |
GetKPIsResponse
GetKPIsResponse holds the 8 KPI tiles plus a server-side
generated_at for client-side staleness display.
| Field | Type | Label | Description |
|---|---|---|---|
| tiles | KPITile | repeated | tiles is ordered (see KPITile.id documentation). Always 8 entries for v1; new tiles will be appended as the proto evolves. |
| generated_at | google.protobuf.Timestamp | generated_at is the server wall-clock at aggregation time. | |
| range_start | google.protobuf.Timestamp | range_start is the resolved query window start (echoed back so the frontend can render "as of …" copy without re-deriving the default window). | |
| range_end | google.protobuf.Timestamp | range_end is the resolved query window end. |
GetPillarSpendRequest
GetPillarSpendRequest filters the pillar rollup. Pillars = org_units rows with unit_type='pillar' AND parent_id IS NULL (top-level by convention pending #1090's structural rule). v1 always queries the caller's full org (the pillar list IS the partitioning). Scope is accepted for symmetry with the rest of the service and to permit a future "pillars under this division" query when #1090 lands.
| Field | Type | Label | Description |
|---|---|---|---|
| scope | MetricsScope | scope filters the aggregation. Pillar/team scopes are accepted on the surface but v1 always returns the caller's full pillar list (the partitioning IS the pillars). #1090 will narrow under a division. | |
| scope_id | string | scope_id is the UUID of the org_unit when scope is pillar/team. | |
| range_start | google.protobuf.Timestamp | range_start defaults to range_end - 30d. | |
| range_end | google.protobuf.Timestamp | range_end defaults to server wall-clock now. |
GetPillarSpendResponse
GetPillarSpendResponse contains the pillar rollup rows.
| Field | Type | Label | Description |
|---|---|---|---|
| pillars | PillarSpendRow | repeated | pillars is ordered by spend descending, ties broken by name asc. |
| range_start | google.protobuf.Timestamp | range_start echoes the resolved window start. | |
| range_end | google.protobuf.Timestamp | range_end echoes the resolved window end. |
GetTimeSeriesRequest
GetTimeSeriesRequest selects one metric, scope, range, and bucket size. Tenant identity comes from the JWT scope claim (org_id); scope_id is only honoured for PILLAR/TEAM scopes (and currently only applied where the underlying source carries a team_id, identical to GetKPIs — see Metrics 5/8 #1116 follow-up).
| Field | Type | Label | Description |
|---|---|---|---|
| metric_id | MetricID | metric_id selects the time-series projection. Required; METRIC_ID_UNSPECIFIED is rejected. | |
| scope | MetricsScope | scope filters the aggregation to org / pillar / team. When unspecified or METRICS_SCOPE_ORG, the caller's org is used. | |
| scope_id | string | scope_id is the UUID of the org_unit (pillar or team). Required when scope is METRICS_SCOPE_PILLAR / METRICS_SCOPE_TEAM. See GetKPIsRequest.scope_id for the v1 limitation note. | |
| range | TimeSeriesRange | range selects the trailing window or CUSTOM. Defaults to TIME_SERIES_RANGE_DAYS_30 when unspecified. | |
| custom_start | google.protobuf.Timestamp | custom_start is the inclusive start of the query window when range == TIME_SERIES_RANGE_CUSTOM. Required for CUSTOM, ignored otherwise. | |
| custom_end | google.protobuf.Timestamp | custom_end is the exclusive end of the query window when range == TIME_SERIES_RANGE_CUSTOM. Required for CUSTOM, ignored otherwise. | |
| granularity | TimeSeriesGranularity | granularity selects the bucket size. Defaults to TIME_SERIES_GRANULARITY_DAY when unspecified. |
GetTimeSeriesResponse
GetTimeSeriesResponse holds the ordered points plus a unit and the resolved range. Empty buckets are omitted (the frontend interpolates or renders zero — same convention as analytics.GetTokenUsageTimeSeries).
| Field | Type | Label | Description |
|---|---|---|---|
| points | TimeSeriesPoint | repeated | points is the ordered (date, value) series. Capped at the per- granularity limit (168/90/13) — the service rejects over-cap requests with InvalidArgument so this field length is a hard ceiling. |
| unit | string | unit is one of {"usd", "ratio", "count", "hours", "seconds"} — the frontend formatter switches on this. Locked to a closed set so client formatters stay stable. | |
| range_start | google.protobuf.Timestamp | range_start is the resolved query window start (UTC). | |
| range_end | google.protobuf.Timestamp | range_end is the resolved query window end (UTC, exclusive). | |
| granularity | TimeSeriesGranularity | granularity is the resolved bucket size (echoed for client display). |
GetWorkflowsBreakdownRequest
GetWorkflowsBreakdownRequest filters the workflows breakdown.
| Field | Type | Label | Description |
|---|---|---|---|
| scope | MetricsScope | scope filters the aggregation. METRICS_SCOPE_ORG (or unspecified) queries the full org. Pillar/team scoping is accepted on the surface; pillar narrowing is wired via workflows.team_id -> org_units.ancestor_path for descendants of the requested unit. | |
| scope_id | string | scope_id is the UUID of the org_unit (pillar or team) when scope is METRICS_SCOPE_PILLAR / METRICS_SCOPE_TEAM. Required in those cases; ignored otherwise. | |
| range_start | google.protobuf.Timestamp | range_start mirrors GetKPIsRequest: defaults to range_end - 30d. | |
| range_end | google.protobuf.Timestamp | range_end mirrors GetKPIsRequest: defaults to server wall-clock now. | |
| top_n | int32 | top_n caps the returned rows. 0 -> default (20). > MaxBreakdownTopN is clamped, not rejected. |
GetWorkflowsBreakdownResponse
GetWorkflowsBreakdownResponse contains the ordered top-N rows.
| Field | Type | Label | Description |
|---|---|---|---|
| rows | WorkflowBreakdownRow | repeated | rows is ordered by runs descending, ties broken by name asc. |
| range_start | google.protobuf.Timestamp | range_start echoes the resolved window start. | |
| range_end | google.protobuf.Timestamp | range_end echoes the resolved window end. |
GuardrailBreakdownRow
GuardrailBreakdownRow is one row of the guardrails breakdown table. "fires" = blocked + allowed. The rule_id grouping derives from agent_audit_log.detail->>'rule_id' for forward compatibility with a canonical guardrail registry; rows without a rule_id fall back to the audit row's action_type as the id, with name = the verb.
| Field | Type | Label | Description |
|---|---|---|---|
| guardrail_id | string | guardrail_id is detail->>'rule_id' or the action_type fallback. | |
| name | string | name is detail->>'rule_name' or guardrail_id when not provided. | |
| fires | int64 | fires = blocked + allowed. | |
| blocked | int64 | blocked is the count of rows whose action_type='action_blocked'. | |
| allowed | int64 | allowed is the count of rows whose action_type='action_auto_executed' for the same rule_id. | |
| trend | double | trend is period-over-period delta on fires, as a signed %. |
KPITile
KPITile is one of the eight tiles surfaced by GetKPIs. The order in the response array matches the headline strip ordering documented in the v3 audit (#1096 §2.11): spend → runs → success → hours saved → backlog → guardrails → agents → cost-per-run.
| Field | Type | Label | Description |
|---|---|---|---|
| id | string | id is a stable identifier the frontend keys off (e.g. "spend", "runs", "success_rate"). Stable across releases — append-only. | |
| label | string | label is the human-readable display name (e.g. "Total spend"). Localised on the frontend; the server returns the canonical English string for the v1 contract. | |
| value | double | value is the scalar numeric value. Cost values are USD; rate values are 0..1; counts are integers represented as double for JSON-fidelity (Connect-Web JSON). Frontend formatters decide currency / percentage / integer presentation per id. | |
| unit | string | unit is one of {"usd", "ratio", "count", "hours"} — the frontend formatter switches on this. Empty when unit is implicit in the id. |
PendingByAgeBucket
PendingByAgeBucket is one row of the pending-approvals histogram. Bucket strings are stable: "<1h", "1-4h", "4-24h", ">24h" — the frontend keys off these literal labels (metrics-data.jsx pendingByAge).
| Field | Type | Label | Description |
|---|---|---|---|
| bucket | string | bucket is one of the literal strings above. | |
| count | int64 | count is the number of pending approvals in the bucket. |
PillarSpendRow
PillarSpendRow is one row of the pillar rollup table. Maps to metrics-data.jsx pillarRollup (lines 161-169) and the pillar-mode columns in metrics.jsx TeamRollupCard.
| Field | Type | Label | Description |
|---|---|---|---|
| pillar_id | string | pillar_id is the org_units.id (UUID as string). | |
| name | string | name is the org_units.name (e.g. "Engineering", "Product"). | |
| agents | int64 | agents is the COUNT(DISTINCT a.id) of agents whose pillar_id matches this org_unit (legacy column on agents — the structural rule from #1090 will replace this with the org_unit_memberships join graph). | |
| runs | int64 | runs is the COUNT(*) of workflow_runs whose owning workflow's team_id sits underneath this pillar (joined via org_units ancestor_path) within the window. | |
| spend | double | spend is SUM(usage_records.cost_usd) for usage records owned by any agent in the pillar over the window. | |
| success_rate | double | success_rate is completed/runs (0..1). 0 when runs == 0. | |
| hours_saved | double | hours_saved is runs * HoursPerWorkflowRunHeuristic (mirrors GetKPIs). Same heuristic applies until tenants supply per- workflow baselines. | |
| trend | double | trend is period-over-period delta on runs, as a signed %. | |
| lead_member_id | string | lead_member_id is one member with role='lead' on the pillar org_unit (org_unit_memberships). Empty when no lead is set. | |
| headcount | int64 | headcount is the COUNT(DISTINCT member_id) of org_unit_memberships rows for this pillar (any role). |
TimeSeriesPoint
TimeSeriesPoint is a single (timestamp, value) datum. Ordered by
date ascending in the response.
| Field | Type | Label | Description |
|---|---|---|---|
| date | google.protobuf.Timestamp | date is the bucket boundary (UTC). For HOUR this is the start of the hour; DAY the start of the day; WEEK the ISO Monday. | |
| value | double | value is the scalar metric value. Cost values are USD; latency values are seconds; rate values are 0..1; counts are integers represented as double for JSON-fidelity (Connect-Web JSON). |
WorkflowBreakdownRow
WorkflowBreakdownRow is one row of the workflows breakdown table. Field shape mirrors the columns rendered in metrics.jsx lines 163-173 (Workflow / Runs / Success / Avg / Esc. / Trend).
| Field | Type | Label | Description |
|---|---|---|---|
| workflow_id | string | workflow_id is the UUID of the underlying workflows row. | |
| name | string | name is the human-readable workflow name (workflows.name). | |
| runs | int64 | runs is the total workflow_runs in the window. | |
| success_rate | double | success_rate is completed/runs (0..1). 0 when runs == 0. | |
| avg_duration_seconds | double | avg_duration_seconds averages (completed_at - started_at) over completed runs only. NaN-protected: 0 when no completed runs. | |
| escalation_rate | double | escalation_rate is the share of workflow_actions for runs in this window whose status is 'escalated', 0..1. | |
| trend | double | trend is the period-over-period delta of runs against the immediately preceding window of equal length, expressed as a signed percentage (-100..+inf). 0 when the prior window is empty. |
MetricID
MetricID is the discriminator for GetTimeSeries. Each value names a single time-series projection. Append-only: existing values keep their tag numbers across releases so the frontend can pin against a stable set.
| Name | Number | Description |
|---|---|---|
| METRIC_ID_UNSPECIFIED | 0 | METRIC_ID_UNSPECIFIED is rejected with InvalidArgument — callers must pick an explicit metric. |
| METRIC_ID_SPEND | 1 | METRIC_ID_SPEND — sum of usage_records.cost_usd per bucket (USD). |
| METRIC_ID_RUNS | 2 | METRIC_ID_RUNS — count of workflow_runs per bucket. |
| METRIC_ID_SUCCESS_RATE | 3 | METRIC_ID_SUCCESS_RATE — completed/total ratio per bucket (0..1). |
| METRIC_ID_APPROVALS_BACKLOG | 4 | METRIC_ID_APPROVALS_BACKLOG — count of governance_approvals.status='pending' anchored at bucket end (snapshot semantic). |
| METRIC_ID_GUARDRAILS_FIRED | 5 | METRIC_ID_GUARDRAILS_FIRED — count of agent_audit_log action_type='action_blocked' per bucket. |
| METRIC_ID_LATENCY_P50 | 6 | METRIC_ID_LATENCY_P50 — p50 of workflow_runs (completed_at - started_at) in seconds per bucket. |
| METRIC_ID_COST_PER_RUN | 7 | METRIC_ID_COST_PER_RUN — spend/runs derived per bucket (USD; 0 when runs=0). |
| METRIC_ID_HOURS_SAVED | 8 | METRIC_ID_HOURS_SAVED — runs * HoursPerWorkflowRunHeuristic per bucket. |
MetricsScope
MetricsScope mirrors AnalyticsService.Scope so callers can switch a metric query between org-wide, a single pillar, or a single team without touching the rest of the request shape. A separate enum is declared (rather than importing analytics.Scope) so MetricsService is not a leaf of analytics.proto — keeps the proto package self-contained for the redesign tracker. Wave-K.2 may later rename "team" → "org units"; the enum naming is intentionally left as TEAM/PILLAR for alignment with the existing analytics surface and the v3 audit doc.
| Name | Number | Description |
|---|---|---|
| METRICS_SCOPE_UNSPECIFIED | 0 | METRICS_SCOPE_UNSPECIFIED defaults to organisation-wide. |
| METRICS_SCOPE_ORG | 1 | METRICS_SCOPE_ORG queries across the entire organisation. |
| METRICS_SCOPE_PILLAR | 2 | METRICS_SCOPE_PILLAR queries within a single pillar (org_unit). |
| METRICS_SCOPE_TEAM | 3 | METRICS_SCOPE_TEAM queries within a single team (org_unit). |
TimeSeriesGranularity
TimeSeriesGranularity selects the date_trunc bucket size for GetTimeSeries. HOUR/DAY/WEEK mirror analytics.Granularity but a separate enum is declared so MetricsService stays self-contained.
| Name | Number | Description |
|---|---|---|
| TIME_SERIES_GRANULARITY_UNSPECIFIED | 0 | TIME_SERIES_GRANULARITY_UNSPECIFIED defaults to DAY. |
| TIME_SERIES_GRANULARITY_HOUR | 1 | TIME_SERIES_GRANULARITY_HOUR — bucket by hour (cap 168 points / 7 days). |
| TIME_SERIES_GRANULARITY_DAY | 2 | TIME_SERIES_GRANULARITY_DAY — bucket by day (cap 90 points). |
| TIME_SERIES_GRANULARITY_WEEK | 3 | TIME_SERIES_GRANULARITY_WEEK — bucket by ISO week (cap 13 points). |
TimeSeriesRange
TimeSeriesRange is the canonical preset for the trends-grid range dropdown. CUSTOM unlocks the optional custom_start / custom_end pair in the request envelope.
| Name | Number | Description |
|---|---|---|
| TIME_SERIES_RANGE_UNSPECIFIED | 0 | TIME_SERIES_RANGE_UNSPECIFIED defaults to DAYS_30. |
| TIME_SERIES_RANGE_DAYS_7 | 1 | TIME_SERIES_RANGE_DAYS_7 — trailing 7 days ending now (UTC). |
| TIME_SERIES_RANGE_DAYS_30 | 2 | TIME_SERIES_RANGE_DAYS_30 — trailing 30 days ending now (UTC). |
| TIME_SERIES_RANGE_DAYS_90 | 3 | TIME_SERIES_RANGE_DAYS_90 — trailing 90 days ending now (UTC). |
| TIME_SERIES_RANGE_CUSTOM | 4 | TIME_SERIES_RANGE_CUSTOM — uses custom_start / custom_end on the request. Both are required when this range is selected. |
MetricsService
MetricsService exposes aggregator RPCs that compose multiple existing reads into a single response for the redesigned Metrics page. All RPCs are tenant-scoped via the JWT-derived scope claims injected by the scope middleware. There is no direct DB access that an existing service does not already permit; this is purely a composition boundary.
| Method Name | Request Type | Response Type | Description |
|---|---|---|---|
| GetKPIs | GetKPIsRequest | GetKPIsResponse | GetKPIs returns the 8-tile headline KPI strip for the calling tenant over the requested range and scope. Composes: 1. total spend (USD) over [range_start, range_end) 2. workflow run count over [range_start, range_end) 3. workflow success rate (completed / total) over the same window 4. hours-saved heuristic = workflow_runs * 0.5 5. approvals backlog (pending count, age-agnostic) 6. guardrails fired (action_blocked count over the window) 7. active agents count (current snapshot, not windowed) 8. cost per run = spend / runs (0 when runs == 0) |
The aggregation reuses the same data sources that AnalyticsService.GetDashboardKPIs / GetWorkflowMetrics / GetCostBreakdown already query, so the security boundary is identical (RLS on every base table). | | GetTimeSeries | GetTimeSeriesRequest | GetTimeSeriesResponse | GetTimeSeries returns a single metric as an array of (timestamp, value) points over the requested range and granularity. Powers the 6-chart trends grid on the redesigned Metrics page (v3 audit #1096 §2.11; prototype docs/design/redesign-2026-04/project/metrics.jsx lines 112-132).
Internally maps to existing time-series sources: SPEND -> usage_records.cost_usd (matches AnalyticsService.GetTokenUsageTimeSeries) RUNS -> workflow_runs aggregated by created_at SUCCESS_RATE -> derived (completed / total) per bucket APPROVALS_BACKLOG -> snapshot count of governance_approvals.status='pending' per bucket GUARDRAILS_FIRED -> agent_audit_log filtered on action_type='action_blocked' LATENCY_P50 -> workflow_runs duration p50 (percentile_cont) COST_PER_RUN -> derived (spend / runs) per bucket HOURS_SAVED -> derived (runs * HoursPerWorkflowRunHeuristic) per bucket
Response is capped at 168 points (HOUR), 90 (DAY), 13 (WEEK). Over-cap requests are rejected with InvalidArgument so the chart grid can never pull an unbounded series. | | GetWorkflowsBreakdown | GetWorkflowsBreakdownRequest | GetWorkflowsBreakdownResponse | GetWorkflowsBreakdown returns the top-N workflows by run volume over the requested window. Powers the "Workflows · by volume" table in the redesigned Metrics page (#1031, v3 audit §2.11). See metrics.jsx prototype lines 163-173 for the rendered shape. | | GetGuardrailsBreakdown | GetGuardrailsBreakdownRequest | GetGuardrailsBreakdownResponse | GetGuardrailsBreakdown returns the top-N guardrails by activity over the window. Source is agent_audit_log rows whose action_type is either 'action_blocked' (rejected) or 'action_auto_executed' (allowed) — the canonical "guardrail intercepted" surface today. Rows are grouped by detail->>'rule_id' (falling back to action_type when no rule id is present). Powers the "Guardrails · by activity" table; see metrics.jsx prototype lines 150-161. | | GetFailuresBreakdown | GetFailuresBreakdownRequest | GetFailuresBreakdownResponse | GetFailuresBreakdown returns the top-N failures (by count) over the window. Source is workflow_runs rows with status='failed'; root_cause is best-effort from the run's context JSONB. Powers the "Top failures" table; see metrics.jsx prototype lines 175-184. | | GetApprovalMetrics | GetApprovalMetricsRequest | GetApprovalMetricsResponse | GetApprovalMetrics returns the operational-health approvals card payload: pending total + age histogram, today's approved/rejected counters, p50/p95 latency, and the top approvers ranked by today's decision count. Powers the "Approvals throughput" card; see metrics.jsx prototype lines 320-356 (ApprovalsCard). | | GetPillarSpend | GetPillarSpendRequest | GetPillarSpendResponse | GetPillarSpend returns per-pillar rollup rows (pillars = unit_type='pillar' org_units, top-level by convention pending the structural rule from #1090). Powers the pillar-mode of the team rollup card on the Metrics page; see metrics.jsx prototype lines 358-435 (TeamRollupCard with mode='pillar') and metrics-data.jsx pillarRollup (lines 161-169). |
Scalar Value Types
| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
|---|---|---|---|---|---|---|---|---|
| double | double | double | float | float64 | double | float | Float | |
| float | float | float | float | float32 | float | float | Float | |
| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
| bool | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | |
| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |