# Get retro analytics

`GET https://evohub.io/api/v1/retro/analytics`

Part of the [Retro API](https://docs.evohub.io/retro.md) reference · operationId `getRetroAnalytics`.

Activity, participation and action-item follow-through across every
retro the caller can see (the same set the board list shows), over a
range: headline totals, a time series, a breakdown by team, one row
per retro held in the range (at most 500; `retros_truncated` says
when there were more), the most agreed-with cards, and optionally the
same totals for a comparison period.

Without `from` and `to` the range is the last 30 UTC days, today
included. A range of 48 hours or less is answered in hourly buckets,
a longer one in daily buckets.

Requires the `retro:board:read` scope.

## Authorization

Any one of:

- `bearerKey`
- `headerKey`

Where:

- `bearerKey`: HTTP Bearer — An EvoHub API key (`evohub_…`) as a bearer token.
- `headerKey`: API key in the header `X-API-Key` — An EvoHub API key (`evohub_…`).

## Query parameters

- `from` (string, example `2026-10-01`): Start of the range: a date (`YYYY-MM-DD`, the start of that UTC day) or an RFC 3339 instant. Send it together with `to`.
- `to` (string, example `2026-10-31`): End of the range: a date (`YYYY-MM-DD`, that UTC day included) or an RFC 3339 instant (exclusive). Must be after `from`, at most 366 days later.
- `compare` (string, one of `previous`, `year`): Also total a comparison period: `previous` (the same length immediately before) or `year` (the same dates a year earlier).
- `team_id` (string, example `none`): Narrow to one team's boards plus the organization-wide ones, or `none` for only the organization-wide ones. It never widens what the caller can see.

## Responses

### 200 — The analytics.

Content type: `application/json`

Type: `object`

- `success` (boolean)
- `data` (RetroAnalytics)
  - `range` (object)
    - `from` (string (date-time))
    - `to` (string (date-time)): Exclusive.
    - `granularity` (string, one of `hour`, `day`)
    - `tz` (string, value `UTC`)
  - `totals` (RetroTotals)
    - `retros` (integer)
    - `closed_retros` (integer)
    - `cards` (integer)
    - `votes` (integer)
    - `comments` (integer)
    - `participation` (Participation)
    - `action_items` (ActionStats)
  - `action_items_now` (object): The visible retros' action items as they stand now.
    - `open` (integer)
    - `overdue` (integer)
    - `with_due_at` (integer)
    - `open_age` (DurationStats)
      - `count` (integer)
      - `p50_seconds` (number | null)
      - `p90_seconds` (number | null)
      - `avg_seconds` (number | null)
    - `oldest_open_since` (string (date-time) | null)
  - `series` (array of object)
    - `bucket` (string (date-time))
    - `retros` (integer)
    - `cards` (integer)
    - `votes` (integer)
    - `comments` (integer)
    - `active_people` (integer)
    - `action_items_created` (integer)
    - `action_items_completed` (integer)
  - `by_team` (array of object & RetroTotals): The totals per team; `team_id` `""` is the organization-wide retros.
    - `team_id` (string)
    - `retros` (integer)
    - `closed_retros` (integer)
    - `cards` (integer)
    - `votes` (integer)
    - `comments` (integer)
    - `participation` (Participation)
      - `participants` (integer): Distinct people (members and share-link guests) who wrote a card, voted or commented.
      - `guest_participants` (integer)
      - `participants_per_retro` (object): Over every retro, empty ones included.
        - `avg` (number | null)
        - `p50` (number | null)
      - `cards_per_participant` (number | null)
      - `votes_per_participant` (number | null)
      - `comments_per_participant` (number | null)
      - `rate` (number | null): Named members who took part divided by named members, over the retros naming at least two people; `null` when none does.
      - `rate_retros` (integer): How many retros `rate` is over.
      - `named_members` (integer)
      - `named_participants` (integer)
    - `action_items` (ActionStats)
      - `created` (integer): Action items created in the range.
      - `completed` (integer): Action items completed in the range.
      - `done_of_created` (integer): Of those created in the range, how many are done now.
      - `follow_through_rate` (number | null): `done_of_created` / `created`.
      - `done_without_date` (integer): Created in the range and done, but with no completion time recorded.
      - `time_to_complete` (DurationStats)
  - `retros` (array of object): One row per retro held in the range, at most 500.
    - `id` (string)
    - `name` (string)
    - `team_id` (string)
    - `status` (string, one of `active`, `closed`)
    - `created_at` (string (date-time))
    - `participants` (integer)
    - `guest_participants` (integer)
    - `named_members` (integer)
    - `named_participants` (integer)
    - `participation_rate` (number | null)
    - `cards` (integer)
    - `votes` (integer)
    - `comments` (integer)
    - `action_items` (integer)
    - `action_items_done` (integer)
  - `retros_truncated` (boolean): `true` when more retros were held than `retros` lists.
  - `top_cards` (array of object): The most agreed-with cards of the range's retros.
    - `card_id` (string)
    - `board_id` (string)
    - `content` (string)
    - `agree` (integer)
    - `disagree` (integer)
  - `compare` (null | object & RetroTotals): The comparison period's totals; `null` without `compare`.
    - One of:
      - null
      - object & RetroTotals
        - `mode` (string, one of `previous`, `year`)
        - `from` (string (date-time))
        - `to` (string (date-time))
        - `retros` (integer)
        - `closed_retros` (integer)
        - `cards` (integer)
        - `votes` (integer)
        - `comments` (integer)
        - `participation` (Participation)
          - `participants` (integer): Distinct people (members and share-link guests) who wrote a card, voted or commented.
          - `guest_participants` (integer)
          - `participants_per_retro` (object): Over every retro, empty ones included.
            - `avg` (number | null)
            - `p50` (number | null)
          - `cards_per_participant` (number | null)
          - `votes_per_participant` (number | null)
          - `comments_per_participant` (number | null)
          - `rate` (number | null): Named members who took part divided by named members, over the retros naming at least two people; `null` when none does.
          - `rate_retros` (integer): How many retros `rate` is over.
          - `named_members` (integer)
          - `named_participants` (integer)
        - `action_items` (ActionStats)
          - `created` (integer): Action items created in the range.
          - `completed` (integer): Action items completed in the range.
          - `done_of_created` (integer): Of those created in the range, how many are done now.
          - `follow_through_rate` (number | null): `done_of_created` / `created`.
          - `done_without_date` (integer): Created in the range and done, but with no completion time recorded.
          - `time_to_complete` (DurationStats)

### 400 — A query parameter breaks a rule (`VALIDATION_FAILED`; `details` names each one): only one of `from` and `to`, a value that is neither a date nor RFC 3339, `to` not after `from`, a range over 366 days, or an unknown `compare`.

Content type: `application/json`

Type: `ValidationFailed`

- `success` (boolean, value `false`)
- `error` (object, required)
  - `code` (string, required, value `VALIDATION_FAILED`)
  - `message` (string, required)
  - `details` (array of object, required)
    - `field` (string)
    - `message` (string)
  - `request_id` (string)

### 401 — No API key was sent, or it is unknown, revoked or expired (`UNAUTHORIZED`).

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 403 — The key lacks the scope this endpoint needs (`FORBIDDEN`).

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 429 — Too many requests (`RATE_LIMITED`). Wait for `Retry-After` seconds.

Headers:

- `Retry-After` (integer): Seconds to wait before retrying.

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 500 — Something went wrong on EvoHub's side (`INTERNAL_ERROR`). Retry later.

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

## Example request

```bash
curl -X GET 'https://evohub.io/api/v1/retro/analytics' \
  -H 'Authorization: Bearer <TOKEN>'
```
