---
title: "Workflow records metrics"
method: POST
path: "/api/workflow-records/metrics"
tags: ["workflow_records"]
---

# Workflow records metrics

`POST /api/workflow-records/metrics`

## Request

- Base URL: `https://beta.getsales.io/playtables/v1`
- URL: `https://beta.getsales.io/playtables/v1/api/workflow-records/metrics`
- Auth: HTTP bearer and API key in header `Team-SID`

Period-bound enrollment/outcome funnel: entered_count (created_at in the half-open [from,to) window), completed/failed/exited counts + completion_rate + avg_hours_to_complete (finished_at clock, same window). group_by one axis (status | workflow_sid | current_step_id | sender_profile_sid | entered_via_source_sid) adds groups[] with counts + metrics per value; current_step_id grouping requires workflow_sid.eq in filter (422). created_at operators in filter are ignored - period owns the window.

Contract:
- MCP tool `get_workflow_records_metrics`, registry package `mcp.playtables/workflow_records`, mount `playtables.journeys`.
- Operation `metrics`, response envelope `metrics`.
- Flags: read only.

## Request body

- GetWorkflowRecordsMetricsRequest: Request body of `get_workflow_records_metrics`.
  - `filter` object: Scopes the rows the metrics are computed over: the search filter operators. Omit for all rows.
    - `sid` object: Prefix-validated wf_rc_ sid(s); also the single-row read (no get tool).
      - `eq` string: Workflow record sid (wf_rc_…).
      - `in` string[]
    - `workflow_sid` object: Progress-board scope (wf_wf_…).
      - `eq` string
      - `in` string[]
    - `workflow_version_sid` object: "Who is still on the old version" (wf_vr_…).
      - `eq` string
      - `in` string[]
    - `people_sid` object: Person card (cr_pp_…); bulk ops take sids from people search first.
      - `eq` string
      - `in` string[]
    - `entered_via_source_sid` object: Per-source cohort (wf_sr_…).
      - `eq` string
      - `in` string[]
    - `sender_profile_sid` object: Per-sender load (ub_sp_…); is_null:true = unpinned (no send steps).
      - `eq` string
      - `in` string[]
      - `is_null` boolean
    - `current_step_id` object: Step column of the board (int step id); meaningful only with a workflow_sid filter.
      - `eq` integer
      - `in` integer[]
    - `status` object
      - `eq` 'waiting_limit' | 'blocked' | 'active' | 'waiting' | 'paused_on_reply' | 'paused_manual' | 'paused_blocked' | 'failed' | 'completed' | 'exited'
      - `ne` 'waiting_limit' | 'blocked' | 'active' | 'waiting' | 'paused_on_reply' | 'paused_manual' | 'paused_blocked' | 'failed' | 'completed' | 'exited'
      - `in` string[]
      - `nin` string[]
    - `last_touch_at` object: Staleness - "no movement since …"; ISO 8601 bounds.
      - `gte` string
      - `lte` string
      - `gt` string
      - `lt` string
    - `finished_at` object: { is_null: true } = live records; datetime bounds = terminal cohort.
      - `gte` string
      - `lte` string
      - `is_null` boolean
    - `created_at` object: Enrollment window; ISO 8601 bounds, combine in one object.
      - `gte` string
      - `lte` string
      - `gt` string
      - `lt` string
    - `deleted_at` object: Default scope is { is_null: true } (live rows); set only by parent cascades.
      - `gte` string
      - `lte` string
      - `is_null` boolean
  - `period` object, required: Required. Period column: created_at (entered_count); outcome metrics clock on finished_at in the same window.
    - `from` string, required: ISO 8601 UTC window start (inclusive).
    - `to` string, required: ISO 8601 UTC window end (exclusive); must be >= from.
  - `group_by` 'status' | 'workflow_sid' | 'current_step_id' | 'sender_profile_sid' | 'entered_via_source_sid': Single axis - adds groups[] with per-value counts + metrics. current_step_id requires workflow_sid.eq in filter.

## Response `200`

`metrics` success envelope.

- GetWorkflowRecordsMetricsResponse
  - `success` true, required
  - `operation` 'metrics', required
  - `metrics` object, required
    - `filter` union: Echo of the request WorkflowRecordSearchFilter (PHP empty map serialises as []).
      - object
      - unknown[]
        - unknown
    - `period` object, required
      - `from` string, required
      - `to` string, required
    - `aggregated` object, required
      - `counts` object, required
        - `total_count` integer, required: Records matching the filter scope.
        - `groups` object, required
          - `status` union, required: The funnel axis - map keyed by the 8 WorkflowRecordStatusEnum values.
            - object
            - unknown[]
              - …
          - `workflow_sid` union, required: Per-workflow distribution across the team.
            - object
            - unknown[]
              - …
          - `current_step_id` union, required: Board columns - keys are STRINGIFIED step ids; read with a workflow_sid filter in scope.
            - object
            - unknown[]
              - …
          - `sender_profile_sid` union, required: Per-sender load; "null" bucket = unpinned (no send steps).
            - object
            - unknown[]
              - …
          - `entered_via_source_sid` union, required: Per-source funnel.
            - object
            - unknown[]
              - …
      - `metrics` object, required
        - `entered_count` integer, required: Records enrolled in the window: created_at in [period.from, period.to).
        - `completed_count` integer, required: finished_at in period AND status = completed - sequences finished successfully.
        - `failed_count` integer, required: finished_at in period AND status = failed (a stoplist hit no longer fails a journey: it blocks it, paused_blocked, and is not in this count).
        - `exited_count` integer, required: finished_at in period AND status = exited - manual/system removals.
        - `completion_rate` number, nullable, required: completed_count / (completed + failed + exited in period); null when the denominator is 0.
        - `avg_hours_to_complete` number, nullable, required: AVG(finished_at - created_at) in hours over completed in period; null when none.
    - `groups` object[]: Present only when group_by was set - one entry per axis value.
      - `key` string, nullable, required: Value of the group_by axis (a status / workflow_sid / current_step_id / sender_profile_sid / entered_via_source_sid value); null = rows without a value on the axis (e.g. unpinned sender).
      - `counts` object, required
        - `total_count` integer, required: Records matching the filter scope.
        - `groups` object, required
          - `status` union, required: The funnel axis - map keyed by the 8 WorkflowRecordStatusEnum values.
            - object
            - unknown[]
              - …
          - `workflow_sid` union, required: Per-workflow distribution across the team.
            - object
            - unknown[]
              - …
          - `current_step_id` union, required: Board columns - keys are STRINGIFIED step ids; read with a workflow_sid filter in scope.
            - object
            - unknown[]
              - …
          - `sender_profile_sid` union, required: Per-sender load; "null" bucket = unpinned (no send steps).
            - object
            - unknown[]
              - …
          - `entered_via_source_sid` union, required: Per-source funnel.
            - object
            - unknown[]
              - …
      - `metrics` object, required
        - `entered_count` integer, required: Records enrolled in the window: created_at in [period.from, period.to).
        - `completed_count` integer, required: finished_at in period AND status = completed - sequences finished successfully.
        - `failed_count` integer, required: finished_at in period AND status = failed (a stoplist hit no longer fails a journey: it blocks it, paused_blocked, and is not in this count).
        - `exited_count` integer, required: finished_at in period AND status = exited - manual/system removals.
        - `completion_rate` number, nullable, required: completed_count / (completed + failed + exited in period); null when the denominator is 0.
        - `avg_hours_to_complete` number, nullable, required: AVG(finished_at - created_at) in hours over completed in period; null when none.
  - `applied_filters` object
  - `meta` object, required
    - `trace_id` string, required: UUID v7; same 128-bit value as the X-Trace-Id header.
    - `span_id` string, required: 16 hex chars, root span of this request.
    - `timestamp` string, required: ISO 8601 UTC (Y-m-dTH:i:sZ), response time.
    - `duration_ms` integer, required: Server-side wall clock.
    - `team_sid` string, nullable: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends.
    - `actor_type` string, nullable: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends.

## Other responses

- `4XX`: MCP error envelope. `error.code` is one of validation_failed, nothing_to_update, not_found, relation_not_found, invalid_transition, limit_exceeded, payment_required, duplicate_rejected, conflict, delete_blocked, unauthorized, forbidden, rate_limited, not_implemented.
- `5XX`: MCP error envelope with `error.code` internal_error or service_unavailable.

---

[API](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables.md) - [All operations](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables/llms.txt) - [OpenAPI document](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-playtables/revisions/aa394506515d?raw)
