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

# Participant Analysis

> Generate a performance dashboard for an individual participant across all their evaluations.

<Warning>
  This endpoint counts against your monthly analytics quota. Each request is counted even if no data is found. Returns `429` when the limit is reached.
</Warning>

<Note>
  Only **COMPLETED** transcriptions with a non-null evaluation score are included. IN\_PROGRESS and FAILED transcriptions are excluded.
</Note>

<Note>
  Results are capped at **20,000 transcriptions**. If `data.metadata.query_limit_reached` is `true`, the dashboard is based on a partial dataset.
</Note>

<Note>
  `data.comparison.delta` may be an empty object `{}` when there is no previous-period data to compare against (e.g. the participant had no calls last month).
</Note>

<Note>
  The optional `start_date` / `end_date` filters (`YYYY-MM-DD`, both inclusive, each independent) scope the **entire response** to a date window — every metric is computed over the filtered set only. They match on each transcription's `created_at`, which is exactly what [`period_date`](/api-reference/transcription/request-upload-url#recording-date-period_date) sets at upload time, so a call uploaded in June but dated April is correctly returned by a Q2 filter. `data.metadata.filtered` tells you whether a filter was applied — distinguishing "no data" from "the filter excluded everything".
</Note>

<Warning>
  **`fail_rate` changed meaning.** In `data.criteria_breakdown`, `fail_rate` (and the new `pass_rate`) are now computed over `evaluated_count` — the calls where the criterion was actually scored — instead of total calls, so they are guaranteed within 0–100 (remove any client-side clamp). The array now lists **every** criterion, including never-failed ones, sorted worst-first with no-data entries last. When `evaluated_count` is `0`, the rates and `avg_score` are `null` — that means **no data, not a perfect score**.
</Warning>

<Note>
  Per-criterion scores are only materialized for transcriptions evaluated **after this feature's release** — there is no backfill. Older calls still count in `kpis`, `charts`, `comparison`, and `raw_data`, but contribute nothing to `criteria_breakdown` or `criteria_evolution` — so `total_calls: 25` alongside a criterion with `evaluated_count: 5` is correct, not a bug.
</Note>

<Note>
  `data.criteria_evolution` is an **object keyed by criterion ID**, not an array — unlike every other collection in the response. Interpret `avg_score` via each criterion's `type`: `0.0`–`1.0` compliance rate for `boolean`/`strict`, `1.0`–`5.0` for `scale` — the two scales must not share a chart axis.
</Note>

<Note>
  Criterion **names repeat across evaluators** — two evaluators can both define a "Manejo de Objeciones" with different IDs. Use the `evaluator_id` / `evaluator_tag` fields on `criteria_breakdown` and `criteria_evolution` entries to attribute and label them.
</Note>


## OpenAPI

````yaml api-reference/openapi-participants.json POST /analytics-participant
openapi: 3.1.0
info:
  title: Heify API — Participants
  description: Endpoints for creating, retrieving, and managing Participants.
  version: 1.0.0
servers:
  - url: https://api.heify.com
security:
  - apiKeyAuth: []
paths:
  /analytics-participant:
    post:
      summary: Participant Analysis
      description: >-
        Generates a performance dashboard for a single participant across all
        evaluators and campaigns they have been evaluated in. Optionally scope
        the whole dashboard to a date window with `start_date`/`end_date` —
        every metric in the response is computed over the filtered set only.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnalyticsParticipantRequest'
            examples:
              AnalyzeParticipant:
                summary: Analyze a participant
                value:
                  participant_id: 580cc0e1-3a60-49d9-8e20-1745252f0aae
              AnalyzeParticipantDateRange:
                summary: Analyze a participant over Q2 only
                value:
                  participant_id: 580cc0e1-3a60-49d9-8e20-1745252f0aae
                  start_date: '2026-04-01'
                  end_date: '2026-06-30'
      responses:
        '200':
          description: Participant analysis generated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnalyticsParticipantResponse'
              examples:
                Success:
                  summary: Participant analysis
                  value:
                    data:
                      metadata:
                        total_records_analyzed: 5
                        query_limit_reached: false
                        filtered: false
                        generated_at: '2026-03-21T14:07:43.791756'
                      kpis:
                        total_calls: 5
                        avg_score: 55.8
                        pass_rate: 80
                        total_critical_fails: 0
                        pending_critical_reviews: 0
                      charts:
                        timeline:
                          - date: '2026-03-03'
                            avg_score: 66
                            volume: 1
                            critical_fails: 0
                          - date: '2026-03-07'
                            avg_score: 53.3
                            volume: 3
                            critical_fails: 0
                          - date: '2026-03-12'
                            avg_score: 52.8
                            volume: 1
                            critical_fails: 0
                      evaluators:
                        - id: a848aaa5-28d6-43eb-bd7c-247f7535aa3a
                          tag: evaluador temporal
                          calls: 1
                          avg_score: 66
                          critical_fails: 0
                          critical_fail_rate: 0
                          consistency: 0
                          avg_duration: 542
                        - id: 2abb5563-dd64-47bb-bb17-94252e168b06
                          tag: pitch temporal
                          calls: 4
                          avg_score: 53.2
                          critical_fails: 0
                          critical_fail_rate: 0
                          consistency: 13.13
                          avg_duration: 587
                      criteria_breakdown:
                        - id: 2faa4a20-7445-4beb-a09e-2af26872dba3
                          name: Saludo Corporativo inicial2
                          type: boolean
                          evaluator_id: 2abb5563-dd64-47bb-bb17-94252e168b06
                          evaluator_tag: pitch temporal
                          evaluated_count: 4
                          fail_count: 4
                          fail_rate: 100
                          pass_count: 0
                          pass_rate: 0
                          avg_score: 0
                        - id: 881fa6dd-12b2-45e8-9705-0c3485421afe
                          name: Saludo Corporativo
                          type: boolean
                          evaluator_id: a848aaa5-28d6-43eb-bd7c-247f7535aa3a
                          evaluator_tag: evaluador temporal
                          evaluated_count: 1
                          fail_count: 1
                          fail_rate: 100
                          pass_count: 0
                          pass_rate: 0
                          avg_score: 0
                        - id: 5718a64e-43e6-4959-90a6-19946b44249c
                          name: Escucha Activa
                          type: scale
                          evaluator_id: 2abb5563-dd64-47bb-bb17-94252e168b06
                          evaluator_tag: pitch temporal
                          evaluated_count: 4
                          fail_count: 1
                          fail_rate: 25
                          pass_count: 3
                          pass_rate: 75
                          avg_score: 3.75
                        - id: 58f6bc79-3eda-4e67-b762-52466145850a
                          name: cierre de inversion
                          type: strict
                          evaluator_id: 2abb5563-dd64-47bb-bb17-94252e168b06
                          evaluator_tag: pitch temporal
                          evaluated_count: 0
                          fail_count: 0
                          fail_rate: null
                          pass_count: 0
                          pass_rate: null
                          avg_score: null
                      criteria_evolution:
                        2faa4a20-7445-4beb-a09e-2af26872dba3:
                          name: Saludo Corporativo inicial2
                          type: boolean
                          evaluator_id: 2abb5563-dd64-47bb-bb17-94252e168b06
                          evaluator_tag: pitch temporal
                          periods:
                            - period: 2026-03
                              avg_score: 0
                              count: 4
                        5718a64e-43e6-4959-90a6-19946b44249c:
                          name: Escucha Activa
                          type: scale
                          evaluator_id: 2abb5563-dd64-47bb-bb17-94252e168b06
                          evaluator_tag: pitch temporal
                          periods:
                            - period: 2026-03
                              avg_score: 3.75
                              count: 4
                      score_distribution:
                        0-20: 0
                        21-40: 1
                        41-60: 2
                        61-80: 2
                        81-100: 0
                      comparison:
                        current_period:
                          calls: 5
                          avg_score: 55.8
                          pass_rate: 80
                        previous_period:
                          calls: 0
                          avg_score: 0
                          pass_rate: 0
                        delta: {}
                      duration_insights:
                        thresholds:
                          p25: 543
                          p75: 609
                        short:
                          count: 2
                          avg_score: 59.4
                        medium:
                          count: 1
                          avg_score: 65
                        long:
                          count: 2
                          avg_score: 47.5
                      trend:
                        direction: insufficient_data
                        recent_avg: null
                        previous_avg: null
                        sample_size: 20
                      degradation_alert: false
                      raw_data:
                        - id: e927a34c-8f2f-4045-8a15-c0f7a182bab2
                          date: '2026-03-12T20:33:14.145386+00:00'
                          score: 52.8
                          critical: false
                          duration: 542
                          evaluator_tag: pitch temporal
                          evaluator_id: 2abb5563-dd64-47bb-bb17-94252e168b06
                          group: null
                          status: COMPLETED
                          failed_criteria:
                            - Saludo Corporativo inicial2
                        - id: caa75e99-53d5-4a8e-8cb2-0521e2fd59ba
                          date: '2026-03-03T20:19:16.427616+00:00'
                          score: 66
                          critical: false
                          duration: 542
                          evaluator_tag: evaluador temporal
                          evaluator_id: a848aaa5-28d6-43eb-bd7c-247f7535aa3a
                          group: PENDING_REVIEW
                          status: COMPLETED
                          failed_criteria:
                            - Saludo Corporativo
components:
  schemas:
    AnalyticsParticipantRequest:
      type: object
      properties:
        participant_id:
          type: string
          format: uuid
          description: >-
            The unique identifier of the participant to analyze. Must belong to
            the authenticated account.
        start_date:
          type: string
          format: date
          example: '2026-04-01'
          description: >-
            Optional. Only analyse transcriptions from this date onwards.
            `YYYY-MM-DD`, **inclusive** — starts at `00:00:00` UTC on this day.
            Matches on each transcription's `created_at` (which `period_date`
            sets at upload time). Month-only values (`2026-04`) are rejected
            with `400`. Omit for no lower bound.
        end_date:
          type: string
          format: date
          example: '2026-06-30'
          description: >-
            Optional. Only analyse transcriptions up to this date. `YYYY-MM-DD`,
            **inclusive** — ends at `23:59:59` UTC on this day. Month-only
            values are rejected with `400`. Omit for no upper bound. A
            `start_date` later than `end_date` is not an error — it simply
            matches nothing and returns the standard empty response. There is no
            `quarter` parameter: a quarter is just a range (`2026-04-01` →
            `2026-06-30`).
      required:
        - participant_id
    AnalyticsParticipantResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            metadata:
              type: object
              description: Report metadata.
              properties:
                total_records_analyzed:
                  type: integer
                  description: >-
                    Number of COMPLETED transcriptions with a non-null
                    evaluation score included in this report.
                query_limit_reached:
                  type: boolean
                  description: >-
                    `true` if results were capped at the maximum query limit
                    (20,000). The dashboard is based on a partial dataset.
                filtered:
                  type: boolean
                  description: >-
                    `true` when a `start_date` and/or `end_date` filter was
                    applied; `false` when the full history was analysed. Lets a
                    client tell "this participant genuinely has no data" apart
                    from "the filter excluded everything".
                generated_at:
                  type: string
                  description: Timestamp when the report was generated (ISO 8601).
            kpis:
              type: object
              description: Top-level performance indicators.
              properties:
                total_calls:
                  type: integer
                  description: Total evaluated transcriptions for this participant.
                avg_score:
                  type: number
                  description: Average evaluation score (0–100).
                pass_rate:
                  type: number
                  description: >-
                    Percentage of transcriptions that passed (score ≥ 50 and no
                    critical fail).
                total_critical_fails:
                  type: integer
                  description: Number of transcriptions with a STRICT criterion failure.
                pending_critical_reviews:
                  type: integer
                  description: >-
                    Critical fails where the transcription group is
                    `PENDING_REVIEW` or `null`.
            charts:
              type: object
              description: Chart data for visualization.
              properties:
                timeline:
                  type: array
                  description: Daily performance evolution, sorted by date.
                  items:
                    type: object
                    properties:
                      date:
                        type: string
                        description: Date (YYYY-MM-DD).
                      avg_score:
                        type: number
                        description: Average score on this date.
                      volume:
                        type: integer
                        description: Number of evaluated calls on this date.
                      critical_fails:
                        type: integer
                        description: Number of critical failures on this date.
            evaluators:
              type: array
              description: >-
                Evaluator/campaign ranking for this participant, sorted by
                average score (descending). Evaluators with zero calls are
                excluded.
              items:
                type: object
                properties:
                  id:
                    type: string
                    description: Evaluator ID.
                  tag:
                    type: string
                    description: Evaluator/campaign name.
                  calls:
                    type: integer
                    description: Number of evaluated calls in this campaign.
                  avg_score:
                    type: number
                    description: Average evaluation score in this campaign.
                  critical_fails:
                    type: integer
                    description: Number of critical failures in this campaign.
                  critical_fail_rate:
                    type: number
                    description: Critical fails as a percentage of total calls.
                  consistency:
                    type: number
                    description: >-
                      Standard deviation of scores. Lower values indicate more
                      consistent performance.
                  avg_duration:
                    type: integer
                    nullable: true
                    description: Average call duration in seconds.
            criteria_breakdown:
              type: array
              description: >-
                Per-criterion performance across all evaluators this participant
                was evaluated with. Contains **every** criterion defined on
                those evaluators — including criteria that never failed — sorted
                by `fail_rate` descending (worst first); entries with no data
                sort last. Rates are computed over `evaluated_count` (calls
                where the criterion was actually scored), **not** total calls,
                so they are always within 0–100. Per-criterion scores are only
                materialized for transcriptions evaluated after this feature's
                release — older calls contribute nothing here (no backfill),
                even though they still count in the global `kpis`. A dashboard
                can legitimately show `total_calls: 25` alongside a criterion
                with `evaluated_count: 5`.
              items:
                type: object
                properties:
                  id:
                    type: string
                    description: >-
                      Criterion ID. The only unique identifier — criterion names
                      repeat across evaluators.
                  name:
                    type: string
                    description: >-
                      Criterion name. **Not unique** across evaluators — use
                      `id`/`evaluator_id` to disambiguate.
                  type:
                    type: string
                    enum:
                      - boolean
                      - scale
                      - strict
                    description: Criterion type. Needed to interpret `avg_score`.
                  evaluator_id:
                    type: string
                    description: >-
                      ID of the evaluator that owns this criterion. Never
                      `null`.
                  evaluator_tag:
                    type: string
                    description: >-
                      Name (`tag`) of the evaluator that owns this criterion.
                      Never `null`.
                  evaluated_count:
                    type: integer
                    description: >-
                      Number of analysed calls in which this criterion was
                      actually scored. **This is the denominator** for
                      `fail_rate` and `pass_rate`. `0` means no data in the
                      analysed range — typically calls evaluated before
                      per-criterion scores were materialized.
                  fail_count:
                    type: integer
                    description: Number of calls where the criterion failed.
                  fail_rate:
                    type: number
                    nullable: true
                    description: >-
                      `fail_count / evaluated_count × 100`. Guaranteed within
                      `[0, 100]`. `null` when `evaluated_count` is `0` — that
                      means **no data, not a perfect score**; rendering it as
                      `0%` would report an unknown as flawless.
                  pass_count:
                    type: integer
                    description: '`evaluated_count − fail_count`.'
                  pass_rate:
                    type: number
                    nullable: true
                    description: >-
                      `pass_count / evaluated_count × 100`. Complementary to
                      `fail_rate` (they sum to 100). `null` when
                      `evaluated_count` is `0`.
                  avg_score:
                    type: number
                    nullable: true
                    description: >-
                      Mean score of the criterion over the analysed range, on
                      its **native scale** per `type`: `0.0`–`1.0` compliance
                      rate for `boolean`/`strict` (`0.80` = met in 80% of
                      calls), `1.0`–`5.0` mean rating for `scale`. The two
                      scales cannot share an axis or a comparison — branch on
                      `type`. `null` when `evaluated_count` is `0`.
            criteria_evolution:
              type: object
              description: >-
                Per-criterion monthly score time series. **An object keyed by
                criterion ID, not an array** — unlike every other collection in
                this response. Sparse by design: only criteria with scored data
                in the analysed range appear, and only months with data appear
                in `periods` (no zero-filled gaps). Months are derived from each
                transcription's `created_at` — i.e. from `period_date` when set
                at upload — and respect `start_date`/`end_date`. `{}` when no
                analysed transcription has materialized per-criterion scores,
                which is expected when all calls were evaluated before this
                feature's release (no backfill).
              additionalProperties:
                type: object
                properties:
                  name:
                    type: string
                    description: Criterion name. Not unique across evaluators.
                  type:
                    type: string
                    enum:
                      - boolean
                      - scale
                      - strict
                    description: Criterion type. Determines the `avg_score` scale.
                  evaluator_id:
                    type: string
                    description: ID of the evaluator that owns this criterion.
                  evaluator_tag:
                    type: string
                    description: >-
                      Name of the owning evaluator — use it to label chart
                      series, since criterion names repeat across evaluators.
                  periods:
                    type: array
                    description: >-
                      Monthly time series, already sorted chronologically
                      ascending. Monthly granularity only — there is no
                      `granularity` parameter; group months client-side for a
                      quarterly view.
                    items:
                      type: object
                      properties:
                        period:
                          type: string
                          description: The month, always `YYYY-MM`.
                        avg_score:
                          type: number
                          description: >-
                            Mean score for this criterion in this month, on its
                            native scale per `type`: `0.0`–`1.0` for
                            `boolean`/`strict`, `1.0`–`5.0` for `scale`.
                        count:
                          type: integer
                          description: >-
                            Number of calls scored for this criterion in this
                            month.
            score_distribution:
              type: object
              description: Fixed-bin histogram of score distribution.
              properties:
                0-20:
                  type: integer
                21-40:
                  type: integer
                41-60:
                  type: integer
                61-80:
                  type: integer
                81-100:
                  type: integer
            comparison:
              type: object
              description: >-
                Month-over-month comparison. `delta` may be an empty object `{}`
                if there is no previous-period data.
              properties:
                current_period:
                  type: object
                  properties:
                    calls:
                      type: integer
                    avg_score:
                      type: number
                    pass_rate:
                      type: number
                previous_period:
                  type: object
                  properties:
                    calls:
                      type: integer
                    avg_score:
                      type: number
                    pass_rate:
                      type: number
                delta:
                  type: object
                  description: >-
                    Difference between current and previous period. Empty `{}`
                    when previous period has no data.
                  properties:
                    avg_score:
                      type: number
                    pass_rate:
                      type: number
            duration_insights:
              type: object
              description: >-
                Score analysis grouped by call duration buckets
                (short/medium/long). Thresholds are computed as the p25 and p75
                of the duration distribution.
              properties:
                thresholds:
                  type: object
                  properties:
                    p25:
                      type: number
                      description: >-
                        25th percentile of call duration (seconds). Calls below
                        this are 'short'.
                    p75:
                      type: number
                      description: >-
                        75th percentile of call duration (seconds). Calls above
                        this are 'long'.
                short:
                  type: object
                  properties:
                    count:
                      type: integer
                    avg_score:
                      type: number
                medium:
                  type: object
                  properties:
                    count:
                      type: integer
                    avg_score:
                      type: number
                long:
                  type: object
                  properties:
                    count:
                      type: integer
                    avg_score:
                      type: number
            trend:
              type: object
              description: >-
                Performance trajectory — compares the most recent N calls vs.
                the N calls before that.
              properties:
                direction:
                  type: string
                  enum:
                    - improving
                    - declining
                    - stable
                    - insufficient_data
                  description: >-
                    `improving` if recent avg > previous avg by >2 pts;
                    `declining` if <−2 pts; `stable` if within ±2 pts;
                    `insufficient_data` if fewer than 2×sample_size total calls.
                recent_avg:
                  type: number
                  nullable: true
                  description: >-
                    Average score of the most recent N calls. `null` if
                    insufficient data.
                previous_avg:
                  type: number
                  nullable: true
                  description: >-
                    Average score of the N calls before that. `null` if
                    insufficient data.
                sample_size:
                  type: integer
                  description: 'N used for the comparison (default: 20).'
            degradation_alert:
              type: boolean
              description: >-
                `true` if the participant's recent performance (last N calls)
                has dropped more than 10 points below their all-time average.
                Acts as an early warning signal for supervisors.
            raw_data:
              type: array
              description: >-
                Full list of individual transcription records included in this
                analysis.
              items:
                type: object
                properties:
                  id:
                    type: string
                    description: Transcription ID.
                  date:
                    type: string
                    description: Transcription date (ISO 8601).
                  score:
                    type: number
                    description: Evaluation score.
                  critical:
                    type: boolean
                    description: '`true` if a STRICT criterion failed.'
                  duration:
                    type: integer
                    nullable: true
                    description: Audio duration in seconds.
                  evaluator_tag:
                    type: string
                    nullable: true
                    description: Evaluator/campaign name.
                  evaluator_id:
                    type: string
                    nullable: true
                    description: Evaluator ID.
                  group:
                    type: string
                    nullable: true
                    description: >-
                      Transcription lifecycle group. See [Transcription
                      group](/core/transcription#group).
                  status:
                    type: string
                    description: Transcription status.
                  failed_criteria:
                    type: array
                    items:
                      type: string
                    description: Names of criteria that failed in this transcription.
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````