# Group failed runs

**GET** `/api/runs/failures`

Groups the failed runs in a range by workflow, step and cause, still-failing first.

Needs [an API token](https://docs.gorunner.io/api/authentication#scopes) with the **Read** permission for runs (`run:read`).

## Parameters

### Query

- `workflowId` · Optional · UUID: One workflow’s runs.
- `environment` · Optional · string: `production` (the default), `test` or `all`.
- `window` · Optional · string: `24h`, `7d`, `30d`, `90d` or `all` (the default). Not with `from` or `to`.

**What are the rarely used parameters?**

- `triggerType` · Optional · string: How it began: `manual`, `webhook`, `cron`, `poll`, `error`, `run_finished`, `event`, `api`, `mcp` or `subflow`.
- `tag` · Optional · string: Runs with exactly this tag, added by the Tags app.
- `from` · Optional · time: Runs started at or after this time (RFC 3339).
- `to` · Optional · time: Runs started before this time. After `from`.

## Read a group

Runs fall in one group when they failed at the same step for the same kind of reason: IDs, quoted values and numbers are taken out, an HTTP status is kept. `reason` is the newest one’s.

`stillFailing`: the workflow is live and its latest finished production run failed this way. Otherwise `workedSince` says when a run first worked again.

`rerunnable` is the runs not yet made good by a retry, and `rerunIds` the oldest 200 of them: send them to [Run many runs again](https://docs.gorunner.io/api/runs/rerun-runs-bulk). List a group’s runs with [List runs](https://docs.gorunner.io/api/runs/list-runs) and `group={key}`.

## Responses

- 200 OK

  The groups, at most 100.

- 400 `VALIDATION_ERROR`

  A filter has a value it doesn’t take. The message names it.

- 403 `FORBIDDEN`

  The token lacks the **Read** permission for runs.

- 500 `INTERNAL_ERROR`

  The groups couldn’t be read. Try again.

Any call can also answer: [`401 UNAUTHORIZED`](https://docs.gorunner.io/errors#UNAUTHORIZED) · [`401 TOKEN_ENDED`](https://docs.gorunner.io/errors#TOKEN_ENDED) · [`403 TOKEN_SURFACE_OFF`](https://docs.gorunner.io/errors#TOKEN_SURFACE_OFF) · [`403 FEATURE_OFF`](https://docs.gorunner.io/errors#FEATURE_OFF) · [`403 EMBED_NOT_ALLOWED`](https://docs.gorunner.io/errors#EMBED_NOT_ALLOWED) · [`403 EMBED_NOT_AVAILABLE`](https://docs.gorunner.io/errors#EMBED_NOT_AVAILABLE) · [`403 ACCOUNT_SUSPENDED`](https://docs.gorunner.io/errors#ACCOUNT_SUSPENDED) · [`429 RATE_LIMITED`](https://docs.gorunner.io/errors#RATE_LIMITED) · [`503 BUSY`](https://docs.gorunner.io/errors#BUSY) · [`503 PAUSED`](https://docs.gorunner.io/errors#PAUSED) · [`503 SERVICE_UNAVAILABLE`](https://docs.gorunner.io/errors#SERVICE_UNAVAILABLE). See [Errors](https://docs.gorunner.io/api/errors#by-status).

Request

**curl**

```bash
GORUNNER="https://api.gorunner.io"
GORUNNER_TOKEN="grt_…"  # needs Read for runs

curl "$GORUNNER/api/runs/failures?window=7d" \
  -H "Authorization: Bearer $GORUNNER_TOKEN"
```

**JavaScript**

```js
const GORUNNER = "https://api.gorunner.io";
const TOKEN = process.env.GORUNNER_TOKEN;

const res = await fetch(
  `${GORUNNER}/api/runs/failures?window=7d`,
  {
    headers: { Authorization: `Bearer ${TOKEN}` },
  },
);
const { data } = await res.json();
```

**Python**

```python
import os, requests

GORUNNER = "https://api.gorunner.io"
TOKEN = os.environ["GORUNNER_TOKEN"]

res = requests.get(
    f"{GORUNNER}/api/runs/failures",
    params={"window": "7d"},
    headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]
```

Response · 200 OK, shortened

```json
{
  "data": {
    "failed": 12,
    "groupCount": 1,
    "groups": [
      {
        "key": "gAAAAB…",
        "workflowId": "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
        "workflowName": "Order intake",
        "workflowStatus": "active",
        "stepKey": "forward",
        "step": "Forward to fulfilment",
        "reason": "the service answered 503",
        "count": 12,
        "firstAt": "2026-10-23T07:10:00Z",
        "lastAt": "2026-10-25T08:59:58Z",
        "latestRunId": "8f0c41d2-6b7e-4c1a-9d3f-2a5b7c9e1f04",
        "stillFailing": true,
        "workedSince": null,
        "retried": 3,
        "retriedWorked": 2,
        "retryOpen": 0,
        "rerunnable": 10,
        "rerunIds": [
          "1d7f3b9e-2c4a-4e6b-8d0f-5a3c9e1b7d26",
          "…"
        ],
        "ranVersionNumber": 3,
        "publishedVersionNumber": 3
      }
    ]
  }
}
```

**Every field**

It takes no body.

### Response · 200 OK

- `data` · object
  - `failed` · integer
  - `groupCount` · integer
  - `groups` · array of objects
    - `key` · string: Opaque. `GET /api/runs?group={key}` lists the group’s runs.
    - `workflowId` · UUID
    - `workflowName` · string
    - `workflowStatus` · string: One of `active`, `paused`, `draft`.
    - `stepKey` · string: Absent when the messages name no step.
    - `step` · string: The step’s display name (published version, else draft, else key).
    - `reason` · string: The newest failure’s reason without the step prefix, at most 300 characters.
    - `count` · integer
    - `firstAt` · time
    - `lastAt` · time
    - `latestRunId` · UUID
    - `stillFailing` · boolean: The workflow is live and its newest finished production run failed in this group.
    - `workedSince` · time or null: When not still failing: the first production run that worked after `lastAt`.
    - `retried` · integer
    - `retriedWorked` · integer
    - `retryOpen` · integer
    - `rerunnable` · integer: `count - retriedWorked - retryOpen`.
    - `rerunIds` · array of UUIDs: The oldest 200 rerunnable runs, oldest first.
    - `ranVersionNumber` · integer: The version the newest failure ran; absent for the draft.
    - `publishedVersionNumber` · integer
    - `publishedAt` · time

Related: [List runs](https://docs.gorunner.io/api/runs/list-runs) · [Run many runs again](https://docs.gorunner.io/api/runs/rerun-runs-bulk) · [Find out why a run failed](https://docs.gorunner.io/build/why-a-run-failed)

Source: https://docs.gorunner.io/api/stats/list-run-failure-groups
