# List workflows

**GET** `/api/workflows`

Returns a page of the workspace’s workflows, with filters and a sort.

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

## Parameters

### Query

- `page` · Optional · integer: The page, from 1. Below 1, or not a number, reads as 1.
- `limit` · Optional · integer: Workflows a page, 1 to 100. The default is 20; anything else reads as 20.
- `q` · Optional · string: Finds words in the name, description, tags and the apps used, by id or name (`stripe`, `Google Sheets`). Ignores case. Not step names.
- `status` · Optional · string: `draft`, `active`, `paused` or `failing`: live, and its latest finished production run failed.
- `tag` · Optional · string: Keeps workflows with this tag, ignoring case. One tag a call.
- `folder` · Optional · string: A folder’s ID, or `none` for workflows in no folder. Only workflows filed directly in it.
- `sort` · Optional · string: `updated` (the default, newest first), `name` (A to Z), `lastrun` (latest run first) or `status` (paused, live, then drafts).
- `view` · Optional · string: `summary` leaves out each row’s `definition` and `runs7d`. Every row has `summary`: the trigger, the apps, the step count and the steps to set up.

**What are the rarely used parameters?**

- `lastRunStatus` · Optional · string: Keeps workflows whose latest production run is in this state: `completed`, `failed`, `running`, `pending`, `cancelled` or `paused`.
- `ids` · Optional · string: Up to 100 workflow IDs, separated by commas. Answers just those, as one page, and ignores every other filter.
- `counts` · Optional · string: `true` adds `counts`: the matching workflows by state, and how many are failing. Not with `ids`.

## What a row holds

Without `view=summary`, each row holds its whole draft `definition`, so a page can run to hundreds of kilobytes. Ask for the summary when you only list them.

`lastRun` is the latest production run, and test runs never count. A failing row adds `failingSince` and `failingRun`, the run that made it fail.

A token without the **Read** permission for runs gets only `status` and `startedAt` of those runs, and no `failingSince`.

No row holds `webhookToken`. Read it with [Get a workflow](https://docs.gorunner.io/api/workflows/get-workflow).

## Responses

- 200 OK

  A page of workflows. A page past the end has no rows, and `pagination` is still filled in.

- 400 `VALIDATION_ERROR`

  `folder` isn’t a folder ID or `none`, `ids` isn’t a list of up to 100 IDs, or `status`, `lastRunStatus`, `sort` or `view` isn’t one of its values.

- 403 `FORBIDDEN`

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

- 500 `INTERNAL_ERROR`

  The list 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 workflows

URL="$GORUNNER/api/"
URL="${URL}workflows?status=active&view=summary&limit=50"
curl "$URL" \
  -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/` +
    `workflows?status=active&view=summary&limit=50`,
  {
    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/workflows",
    params={
      "status": "active",
      "view": "summary",
      "limit": 50,
    },
    headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]
```

Response · 200 OK, shortened

```json
{
  "data": [
    {
      "id": "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
      "name": "Order intake",
      "status": "active",
      "triggerType": "webhook",
      "hasUnpublishedChanges": false,
      "folderId": "7a2c9e41-5b3d-4f08-a6e1-2d9c4b7f0e13",
      "tags": [
        "orders"
      ],
      "createdByName": "Ada Lovelace",
      "updatedAt": "2026-10-24T16:40:00Z",
      "failing": false,
      "summary": {
        "trigger": {
          "pieceId": "webhook",
          "actionId": "catch_webhook",
          "displayName": "Catch Webhook"
        },
        "pieces": [
          {
            "id": "webhook",
            "name": "Webhook"
          },
          {
            "id": "http",
            "name": "HTTP Request"
          }
        ],
        "stepCount": 1,
        "needsSetup": 0
      },
      "lastRun": {
        "id": "8f0c41d2-6b7e-4c1a-9d3f-2a5b7c9e1f04",
        "status": "completed",
        "startedAt": "2026-10-25T08:59:58Z",
        "finishedAt": "2026-10-25T09:00:00Z"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "totalPages": 1
  }
}
```

**Every field**

It takes no body.

### Response · 200 OK

- `data` · array of objects: `[]` when the requested page is past the last one.
  - `id` · UUID
  - `name` · string
  - `description` · string
  - `definition` · object: The workflow definition, and the single most important shape in this API. A workflow definition: its fields are in [Workflow definition](https://docs.gorunner.io/api/workflow-definition).
  - `status` · string: `draft` = never published. `active` = live, triggers registered and the webhook URL reachable. `paused` = was live, now switched off. Server-owned: it moves only through publish/unpublish, never through a create or update body. One of `draft`, `active`, `paused`.
  - `triggerType` · string: Mirrors `definition.trigger.type` for dispatch. Derived server-side from the definition by every update and publish, so treat it as read-only in practice. A create that sends its own value keeps it until then, unchecked. One of `manual`, `cron`, `webhook`, `callable`, `event`, `error`, `run_finished`, `poll`.
  - `cronExpression` · string or null: Nothing derives or reads this: the schedule runs from the trigger’s own `config`, and `nextRunAt` projects from it too. Null unless a caller wrote one with `PUT /api/workflows/{id}`.
  - `webhookToken` · string or null: The secret segment of the public webhook URL. Made by the first update of a webhook workflow, or at publish; absent until then (so absent in the answers of create, duplicate and import). Returned ONLY to a caller holding `workflow:run`: by `GET /api/workflows/{id}` and in the answers of update, publish, unpublish and restore (rotate answers it to its caller). It is never present on a list row, an export or a version snapshot. It is a bearer capability against an unauthenticated endpoint — possessing it is the ability to fire this workflow in production. Rotate it with `POST /api/workflows/{id}/webhook/rotate`.
  - `latestVersionId` · UUID or null: The published snapshot production executes. Absent means nothing has ever been published.
  - `definitionRev` · integer: Optimistic-lock revision, bumped by every definition save. Populated by `GET /api/workflows/{id}`, by the update response and by the answers of create, duplicate and import; absent (0) on list rows. Send it back as `expectedRev` on an update to refuse a clobbering write.
  - `hasUnpublishedChanges` · boolean: The draft has moved ahead of the published version. Computed on the single-workflow read and on every list row; always `false` for a workflow never published.
  - `folderId` · UUID or null: Absent/null = uncategorized. Changed only through `PUT /api/workflows/{id}/folder`.
  - `tags` · array of strings: Changed only through `PUT /api/workflows/{id}/tags` or the bulk `addTags` operation — never through the workflow update body.
  - `testPayloads` · object or null: The saved test-fixture library, stored outside the definition so publish and version restore never reset it.
  - `pinnedData` · object or null: The pin library (`{"pins": {"<stepName>": {"data": …, "pinnedAt": …, "configHash": …, "staleAt": …, "pieceId": …}}}`). A pinned step emits its fixture instead of executing, and only on test runs. `configHash`, `staleAt` and `pieceId` are the server’s: a step whose config changed keeps its pin marked stale, and a stale pin is not replayed (the step runs); a step whose app was replaced loses it.
  - `workspaceId` · UUID: Stamped from the credential’s scope; never accepted from a request body.
  - `orgId` · UUID
  - `createdBy` · UUID or null
  - `createdByName` · string: List rows only: the creator’s name, else their email address. Absent when the creator is unknown or not a member of the organization (staff who built it on a support visit, someone since removed), and on every row for an embedded person whose installation switched `presence` off (an embedded person is never told an address).
  - `createdAt` · time
  - `updatedAt` · time: When the workflow itself was last edited. Publishing, pausing, tagging and filing it are not edits and leave it alone.
  - `failing` · boolean: List rows only: the workflow is live (`status: active`) and its latest FINISHED production run (one that worked or failed) failed. A later run that is waiting, running, held or was cancelled changes nothing, and test runs never count. A paused or draft workflow is never failing.
  - `failingSince` · time: Only on a failing row: when the first production run failed after the last one that worked (runs cancelled, held or running in between neither break nor begin the streak). Left out for a caller without `run:read`.
  - `failingRun` · object: Only on a failing row: the run that decided it, its latest finished production run, with why it failed. It is `lastRun` unless a later run is waiting, running, held or was cancelled. Without `run:read` it carries `status` and `startedAt` alone.
    - `id` · UUID
    - `status` · string: One of `failed`.
    - `startedAt` · time
    - `finishedAt` · time or null
    - `error` · object: Why it failed, as `lastRun.error` says it.
      - `step` · string or null
      - `stepKey` · string or null
      - `message` · string
  - `summary` · object: List rows only: what a list draws instead of the definition.
    - `trigger` · object or null: The chosen trigger; null while none is chosen.
      - `pieceId` · string
      - `actionId` · string
      - `displayName` · string
    - `pieces` · array of objects: The apps the workflow uses, each once, in walk order (the trigger first), with their display names.
      - `id` · string
      - `name` · string
    - `stepCount` · integer: Steps after the trigger, across branch arms, loop bodies and error paths.
    - `needsSetup` · integer: Steps (and a chosen trigger) the editor marks to configure: `valid: false`, or no app and action chosen.
  - `runs7d` · array of integers: List rows without `view=summary` only: counted runs per UTC day over the last seven days, oldest first (production runs that are not a sub-flow’s, cancelled runs left out).
  - `nextRunAt` · time: Next fire time of an active schedule workflow, projected from its cron in the trigger’s own timezone. Populated by the list endpoint.
  - `lastRun` · object or null: The most recent PRODUCTION run’s outcome (test runs never count). Populated on list rows only. Without `run:read` it carries `status` and `startedAt` alone.
    - `id` · UUID
    - `finishedAt` · time or null
    - `error` · object: Only when `status` is `failed`: why.
      - `step` · string or null: The display name of the step it failed at; null when the reason names no step.
      - `stepKey` · string or null
      - `message` · string: The reason’s first clause, at most 160 characters.
    - `status` · string: One of `pending`, `running`, `completed`, `failed`, `cancelled`, `paused`.
    - `startedAt` · time
- `pagination` · object: Pagination block returned alongside a workflow list.
  - `page` · integer
  - `limit` · integer: The limit ACTUALLY applied after coercion, not necessarily the one you asked for.
  - `total` · integer: Total matching workflows across all pages.
  - `totalPages` · integer
- `counts` · object: Only with `counts=true`: the list’s workflows by state, and how many of them are failing (live, and the latest finished production run failed), under the same filters as the list.
  - `active` · integer
  - `paused` · integer
  - `draft` · integer
  - `failing` · integer

**What else is on each row?**

`createdByName` is who made it, by name or email. It is missing when that person is unknown or has left. `nextRunAt` is the next time a live schedule fires. `runs7d` counts production runs for each of the last 7 days, oldest first, in UTC; cancelled runs and sub-flow runs aren’t counted.

Related: [Get a workflow](https://docs.gorunner.io/api/workflows/get-workflow) · [List tags](https://docs.gorunner.io/api/workflows/list-workflow-tags) · [List folders](https://docs.gorunner.io/api/folders/list-folders) · [Pagination](https://docs.gorunner.io/api/pagination)

Source: https://docs.gorunner.io/api/workflows/list-workflows
