> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowyte.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Query one metric over a filter window

> Compute a single metric over a time window and a set of filter lenses, returning `{ series[], total, delta, n, unit, format }` (plus a `breakdown[]` for non-scalar metrics). `total` is the scalar value for count/ratio/avg metrics and `null` for distribution/percentile metrics (whose per-slice numbers live in `breakdown[]`). `n` is the honest sample count behind the number — a client should refuse to over-claim below a metric's minimum-n. `series[]` is the tz-aware per-bucket trend (empty for point-in-time backlog metrics).
Filters (all optional): `from`/`to` bound the window as a half-open `[from, to)` range (RFC3339); `tz` is the IANA timezone used to bucket the series (default UTC), which fixes day-boundary drift; `bucket` sets the series granularity (day|week|month|hour, default day); `channel` restricts to one channel; `agent` and `queue` are INDEPENDENT lenses (both may be set — they AND together, one narrowing to a single agent, the other to a single queue); `minN` overrides the metric's low-volume suppression floor. Applying a lens the metric does not declare (see the catalog's `dimensions`) is a 400. `compare=true` additionally computes `delta` = current minus the immediately preceding equal-length window (scalar metrics with a bounded window only; otherwise `delta` is null).




## OpenAPI

````yaml /openapi.yaml get /metrics/{id}
openapi: 3.1.0
info:
  title: Flowyte V2 Control-Plane REST API
  version: 1.0.0
  description: >-
    The single REST API for the Flowyte platform (base path `/api/v1`).
    Authenticate every request with a secret API key — `Authorization: Bearer
    flowyte_sk_…` — and each operation lists the scope the key must hold.
    Successful responses use the `ApiResponse<T>` envelope; list responses use
    cursor-based `PaginatedResponse<T>`. Errors are RFC 9457 problem+json.
    Streaming endpoints return Server-Sent Events
    (`event:<type>\ndata:<json>\n\n`, terminating with `event: done`).
  contact:
    name: Flowyte Platform
  license:
    name: Proprietary
servers:
  - url: /api/v1
    description: Flowyte control-plane (versioned URI; additive in v1).
security:
  - apiKey: []
tags:
  - name: Agents
    description: The single user-facing entity.
  - name: Support
    description: In-app "Get help" form → support inbox email.
  - name: Skills
    description: >
      Agent capabilities / tools. A skill is ONE atomic action — typically a
      single API call the agent makes in one step (look up an order, create a
      record, send a message, transfer the call). Use a skill when the task is a
      single step. When a task needs several details gathered across turns
      BEFORE acting, build a Playbook (which gathers the inputs and then calls
      skills) — see the Playbooks tag.
  - name: Integrations
    description: Native OAuth integrations.
  - name: Knowledge
    description: RAG knowledge sources & preview.
  - name: Playbooks
    description: >
      Multi-turn conversation scripts — a node graph (gather → confirm → branch)
      the agent follows to collect several inputs IN ORDER across turns before
      acting. Build a playbook when a single skill call isn't enough because the
      agent must gather MANY details first, or run a SEQUENCE of skills, before
      it can finish (e.g. take a full service request: gather the problem,
      address, and time, confirm, THEN file it; qualify a lead; a multi-step
      intake). A playbook does NOT call an integration itself — it owns the
      conversation and holds the state across turns; the actual action is
      performed by the SKILL(s) it gathers the inputs for. So a playbook
      ORCHESTRATES skills. Rule of thumb — one API call → a Skill; "gather N
      things in order, then submit" → a Playbook that drives the conversation
      and calls the skill(s) at the end.
  - name: Variables
    description: >
      The agent-wide interaction-variable registry — DERIVED at read time from
      the agent's playbooks and skills (collect slots, skill output bindings,
      {var} placeholders) and merged with a thin annotation overlay (notes,
      declared type hints, manual declarations). Read-only observation, NEVER a
      gate: it never validates a reference and never affects authoring, publish,
      or runtime behaviour.
  - name: Guardrails
    description: Deterministic guardrail policies & caller verification.
  - name: Numbers
    description: Phone numbers / DIDs.
  - name: SMS
    description: >
      SMS Hub — A2P 10DLC self-serve registration (brand + campaign), the free
      AI compliance review, per-number SMS enablement, and the consent/opt-out
      surface (suppressions, contacts, TCPA proof export). Texting is US-only; a
      number must be SMS-enabled and the org's 10DLC registration active before
      it can send.
  - name: Test
    description: Test, simulate, talk-token, probe.
  - name: Observe
    description: Post-call analytics, conversations, receipts, transcripts.
  - name: Billing
    description: Plans, wallet, usage, fixed phrases.
  - name: Voices
    description: Voice catalog.
  - name: ApiKeys
    description: Developer / API keys.
  - name: Webhooks
    description: Webhook endpoints & deliveries.
  - name: Records
    description: >
      Caller Context Store — pre-synced external records the agent greets a
      caller from, plus the learned object-type field registry. Fed by the
      Zapier app's Create/Update Record action and directly by this REST API;
      read at call time by the context_lookup skill (a sub-100ms local lookup,
      no Zapier round-trip).
  - name: AuditLogs
    description: API/key activity logs.
  - name: Chat
    description: Chat channel — sessions, messages, OpenAI-compatible, widget.
  - name: PublishableKeys
    description: Browser-safe, one-agent publishable keys.
  - name: Widget
    description: Embed widget config + snippet.
  - name: Uploads
    description: Multipart file uploads backing file_id params
  - name: Meta
    description: Platform metadata (language/SKU/tier capabilities).
  - name: Outbound
    description: >
      Outbound voice — contact lists (import + scrub) and campaigns (create,
      launch, pause/resume/cancel). Launch schedules one call attempt per valid
      contact; a background worker dials them. This release dials informational
      campaigns only.
  - name: OAuth2
    description: >
      Flowyte's minimal OAuth2 authorization server. The Flowyte Zapier app is
      the first registered client. The authorization-code grant (confidential
      client, optional PKCE) issues org-scoped SERVICE tokens (flowyte_oat_…)
      that ride the SAME scope matrix as an sk key. The public
      authorize/token/revoke endpoints are served at the ORIGIN ROOT (NOT under
      /api/v1 — see each path's `servers` override); consent + disconnect are
      /api/v1.
paths:
  /metrics/{id}:
    parameters:
      - name: id
        in: path
        required: true
        description: A metric id from the catalog (e.g. `containment`). Unknown ids → 404.
        example: containment
        schema:
          type: string
    get:
      tags:
        - Observe
      summary: Query one metric over a filter window
      description: >
        Compute a single metric over a time window and a set of filter lenses,
        returning `{ series[], total, delta, n, unit, format }` (plus a
        `breakdown[]` for non-scalar metrics). `total` is the scalar value for
        count/ratio/avg metrics and `null` for distribution/percentile metrics
        (whose per-slice numbers live in `breakdown[]`). `n` is the honest
        sample count behind the number — a client should refuse to over-claim
        below a metric's minimum-n. `series[]` is the tz-aware per-bucket trend
        (empty for point-in-time backlog metrics).

        Filters (all optional): `from`/`to` bound the window as a half-open
        `[from, to)` range (RFC3339); `tz` is the IANA timezone used to bucket
        the series (default UTC), which fixes day-boundary drift; `bucket` sets
        the series granularity (day|week|month|hour, default day); `channel`
        restricts to one channel; `agent` and `queue` are INDEPENDENT lenses
        (both may be set — they AND together, one narrowing to a single agent,
        the other to a single queue); `minN` overrides the metric's low-volume
        suppression floor. Applying a lens the metric does not declare (see the
        catalog's `dimensions`) is a 400. `compare=true` additionally computes
        `delta` = current minus the immediately preceding equal-length window
        (scalar metrics with a bounded window only; otherwise `delta` is null).
      operationId: queryMetric
      parameters:
        - name: from
          in: query
          description: Window start (RFC3339); window is half-open [from, to).
          example: '2026-06-01T00:00:00.000Z'
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          description: Window end (RFC3339, exclusive).
          example: '2026-07-01T00:00:00.000Z'
          schema:
            type: string
            format: date-time
        - name: compare
          in: query
          description: >-
            Also compute `delta` = current minus a baseline window (scalar call
            metrics with a bounded window only). Modes: `prev` (or any truthy
            legacy value — true/1/yes) = the immediately preceding EQUAL-LENGTH
            window [from-Δ, from); `lastweek` = the SAME-length window shifted
            back exactly 7 days [from-7d, to-7d] (same-period-last-week).
            Blank/other = no delta.
          example: prev
          schema:
            type: string
            enum:
              - prev
              - lastweek
              - 'true'
        - name: tz
          in: query
          description: >-
            IANA timezone for series bucketing (default UTC). Fixes day-boundary
            drift.
          example: America/New_York
          schema:
            type: string
        - name: bucket
          in: query
          description: Series granularity (default day).
          example: day
          schema:
            type: string
            enum:
              - day
              - week
              - month
              - hour
        - name: channel
          in: query
          description: Restrict to one channel.
          example: voice
          schema:
            type: string
            enum:
              - voice
              - chat
              - sms
        - name: agent
          in: query
          description: Restrict to one agent id. Independent of queue (they AND together).
          example: agt_4f9c2b7e10a24d51
          schema:
            type: string
        - name: queue
          in: query
          description: Restrict to one queue id. Independent of agent (they AND together).
          example: queue_sales
          schema:
            type: string
        - name: minN
          in: query
          description: Override the metric low-volume suppression floor.
          example: 20
          schema:
            type: integer
            minimum: 0
      responses:
        '200':
          description: The compiled metric result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse_MetricQueryResult'
              example:
                success: true
                data:
                  id: containment
                  unit: percent
                  format: 0.0%
                  total: 0.79
                  delta: 0.03
                  'n': 1240
                  series:
                    - bucket: '2026-06-29T00:00:00.000Z'
                      value: 0.77
                      'n': 180
                    - bucket: '2026-06-30T00:00:00.000Z'
                      value: 0.8
                      'n': 205
                    - bucket: '2026-07-01T00:00:00.000Z'
                      value: 0.81
                      'n': 198
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
        - apiKey:
            - analytics:read
components:
  schemas:
    ApiResponse_MetricQueryResult:
      allOf:
        - $ref: '#/components/schemas/ApiResponseBase'
        - type: object
          required:
            - data
          properties:
            data:
              $ref: '#/components/schemas/MetricQueryResult'
    ApiResponseBase:
      type: object
      required:
        - success
      properties:
        success:
          type: boolean
        message:
          type: string
        errors:
          type: array
          items:
            type: object
            required:
              - field
              - message
            properties:
              field:
                type: string
              message:
                type: string
    MetricQueryResult:
      type: object
      description: >
        The compiled result of queryMetric. `total` is the scalar value for
        count/ratio/avg metrics and null for distribution/percentile metrics
        (whose per-slice numbers live in `breakdown[]`). `n` is the honest
        sample count behind the number. `series[]` is the tz-aware per-bucket
        trend (empty for point-in-time backlog metrics). `delta` is populated
        only when ?compare= was requested on a scalar metric with a bounded
        window.
      required:
        - id
        - unit
        - format
        - 'n'
        - series
      properties:
        id:
          type: string
        unit:
          type: string
        format:
          type: string
        total:
          type:
            - number
            - 'null'
          description: The scalar value; null for distribution/percentile metrics.
        delta:
          type:
            - number
            - 'null'
          description: >-
            total minus the previous equal-length window; null unless ?compare=
            on a scalar metric.
        'n':
          type: integer
          description: Sample count (the denominator) behind the number.
        unscored:
          type: integer
          description: >
            Honesty disclosure: how many counted conversations are NOT YET
            analyzed (pending) for a metric that scores un-analyzed calls as
            failures (goal_completion) or drops them from an average
            (sentiment_avg). Present only for metrics with an unscored notion,
            so a client can banner "N pending analysis" rather than
            over-claiming. Distribution metrics (sentiment mix) instead carry
            this as an `unscored` breakdown row.
        series:
          type: array
          items:
            $ref: '#/components/schemas/MetricSeriesPoint'
        breakdown:
          type: array
          description: >-
            Non-scalar detail: distribution bucket counts (key = bucket name),
            per-language percentile rows (key = language), or categorical/topic
            rows (key = category, value = volume, delta = trend vs the prior
            window). Absent for plain scalar metrics.
          items:
            $ref: '#/components/schemas/MetricBreakdownRow'
    ProblemDetails:
      description: RFC 9457 problem+json.
      type: object
      properties:
        type:
          type: string
          format: uri
          default: about:blank
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        instance:
          type: string
        code:
          type: string
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
    ApiResponse_Void:
      allOf:
        - $ref: '#/components/schemas/ApiResponseBase'
        - type: object
          properties:
            data:
              type:
                - object
                - 'null'
    MetricSeriesPoint:
      type: object
      description: >-
        One point of a metric trend. `group` names the sub-series (distribution
        bucket name, or latency language) and is empty for a plain scalar
        series.
      required:
        - 'n'
      properties:
        bucket:
          type: string
          format: date-time
          description: The tz-aware period start (RFC3339).
        group:
          type: string
        value:
          type:
            - number
            - 'null'
        'n':
          type: integer
    MetricBreakdownRow:
      type: object
      description: >-
        One slice of a non-scalar total: a distribution bucket, a percentile
        language/stage row, or a categorical/topic row.
      required:
        - key
        - 'n'
      properties:
        key:
          type: string
          description: >-
            Bucket name (distribution), language (per-language latency),
            pipeline stage (latency_by_stage), or category (categorical/topics).
        value:
          type:
            - number
            - 'null'
        'n':
          type: integer
        delta:
          type:
            - number
            - 'null'
          description: >-
            Categorical only: trend vs the immediately preceding equal-length
            window (value minus the prior window volume). Absent unless the
            query carried a bounded window.
        valueP95:
          type:
            - number
            - 'null'
          description: >-
            latency_by_stage only: the p95 (tail) for the stage, alongside
            `value` which carries the p50. Absent for every other breakdown
            kind.
        extras:
          $ref: '#/components/schemas/MetricRowExtras'
    MetricRowExtras:
      type: object
      description: >
        calls_by_number only: the per-tracking-number marketing-attribution
        aggregate pack a CategoricalExtras metric attaches to each breakdown row
        alongside the headline call count. All counts are over the current
        window. Absent for every other breakdown kind.
      required:
        - uniqCallers
        - newCallers
        - leads30
        - leads60
        - leads90
        - junk
        - goalsMet
      properties:
        uniqCallers:
          type: integer
          description: Distinct caller numbers in the window.
        newCallers:
          type: integer
          description: >-
            Distinct callers whose first-ever call to this org landed in the
            window.
        leads30:
          type: integer
          description: Calls that lasted at least 30 seconds (and ≥10s).
        leads60:
          type: integer
          description: Calls that lasted at least 60 seconds (and ≥10s).
        leads90:
          type: integer
          description: Calls that lasted at least 90 seconds (and ≥10s).
        junk:
          type: integer
          description: Calls under 10 seconds.
        avgDurationS:
          type:
            - number
            - 'null'
          description: >-
            Average call duration in seconds (null when the number has no
            counted calls).
        goalsMet:
          type: integer
          description: >-
            Calls where the AI reached the configured goal
            (call_summaries.goal_completed).
        trend:
          type: array
          items:
            type: integer
          description: >-
            Per-day call counts over the window (chronological, dense, UTC
            days). Present for the top-8 numbers by volume only.
  responses:
    ValidationError:
      description: Validation failure (errors[] populated on the envelope).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
        application/json:
          schema:
            $ref: '#/components/schemas/ApiResponse_Void'
    Unauthorized:
      description: Missing or invalid API key.
      headers:
        WWW-Authenticate:
          schema:
            type: string
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    Forbidden:
      description: Org mismatch / RLS / insufficient scope / origin not allowed.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    NotFound:
      description: Resource not found in org scope.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
  securitySchemes:
    apiKey:
      type: oauth2
      description: >
        Flowyte secret API key (`Authorization: Bearer flowyte_sk_live_…`).
        Scope-gated; is scoped to your organization — a key can never reach
        another tenant. The listed scopes in each operation's `apiKey`
        requirement are the scopes that key must hold. The `tokenUrl` is
        nominal: keys are minted in the dashboard.
      flows:
        clientCredentials:
          tokenUrl: /api/v1/api-keys
          scopes:
            agents:read: Read agents.
            agents:write: Create/update/delete agents, publish, rollback.
            knowledge:read: Read knowledge sources & preview.
            knowledge:write: Add/remove knowledge sources, uploads.
            skills:read: Read skills & skill-types.
            skills:write: Create/update/delete skills.
            playbooks:read: Read playbooks & graphs.
            playbooks:write: Create/update/delete playbooks & graphs.
            guardrails:read: Read guardrail policies & caller-verification.
            guardrails:write: Update guardrail policies & caller-verification.
            numbers:read: Read phone numbers / search availability.
            numbers:write: Purchase / assign / release numbers.
            sms:read: Read the org's SMS (10DLC) registration status and numbers.
            sms:write: Save/submit the 10DLC registration and toggle numbers for SMS.
            outbound:read: Read outbound contact lists and campaigns.
            outbound:write: >-
              Create/import contact lists, create/launch/pause/resume/cancel
              outbound campaigns, and enqueue single outbound calls.
            integrations:read: Read connected native integrations (status only — never tokens).
            integrations:write: Discover schemas, set data scoping, and disconnect a connection.
            integrations:connect: >-
              Connect a data source (submit credentials / begin OAuth) — a
              SEPARATE, higher-privilege scope because connecting INGESTS
              credentials and opens a new egress path; a discover/scope/author
              key need not carry it.
            records:read: >-
              Read the pre-synced Caller Context Store records and object-type
              field registry.
            records:write: >-
              Upsert / delete / bulk-import / purge caller-context records and
              edit type metadata.
            calls:read: Read conversations, receipts, transcripts, analytics.
            analytics:read: >-
              Read the Observe history list, per-agent analytics (the
              answer-rate summary), the raw knowledge-gap list, and the metric
              catalog + per-metric queries + metric drill-downs.
            analytics:write: >-
              Curate knowledge gaps (dismiss / mark in-progress). [M4 —
              reserved]
            dashboards:read: List and read saved Observe reporting dashboards.
            dashboards:write: >-
              Create, update (full-document replace), and delete Observe
              reporting dashboards.
            reports:read: List and read Observe scheduled reports (report schedules).
            reports:write: >-
              Create, update, and delete Observe scheduled reports (a delete
              stops a recurring digest).
            billing:read: Read plans, wallet, usage.
            audit:read: Read API/key activity logs.
            webhooks:write: Manage webhook endpoints.
            keys:write: Manage secret API keys.
            chat:read: Read chat sessions & messages.
            chat:write: Create chat sessions & send messages (server-side).
            widgets:read: Read widget config & embed snippet.
            widgets:write: Update widget config.
            pubkeys:read: Read publishable keys.
            pubkeys:write: Manage publishable keys.
            phone:read: >-
              Read Flowyte Phone config — settings, ring targets/queues +
              rosters, contacts, block-list, own presence.
            phone:write: >-
              Update Flowyte Phone config — settings, ring targets/queues +
              members, contacts, block-list.
            presence:write: Set your own softphone presence (available/away/dnd/…).
            team:read: Read the team — softphone seats and the team presence roster.
            team:write: >-
              Manage softphone seats — invite, update, and deactivate team
              members.
            escalation_destinations:read: Read external-agent (AI Harness) escalation destinations.
            escalation_destinations:write: >-
              Create, update, delete, and rotate the signing secret of
              escalation destinations.
            escalation_policies:read: Read an agent's escalation routing policy. [ — reserved]
            escalation_policies:write: Update an agent's escalation routing policy. [ — reserved]
            escalations:read: >-
              Read escalation sessions and their message history (connector). [
              — reserved]
            escalations:claim: >-
              Claim an escalation session and extend its lease (connector). [ —
              reserved]
            escalations:respond: >-
              Post messages into a claimed escalation session (connector). [ —
              reserved]
            escalations:resolve: >-
              Resolve, return, or request human takeover of an escalation
              (connector). [ — reserved]

````