# Change many workflows at once

**POST** `/api/workflows/bulk`

Does one thing to up to 200 workflows: put them live, pause, delete, tag or move them.

Needs [an API token](https://docs.gorunner.io/api/authentication#scopes) with the **Write** permission for workflows (`workflow:write`), and each `op` its own: **Delete** for `delete`, **Publish** for `activate` and `pause`.

## Parameters

### Body

- `ids` · Required · array of UUIDs: Up to 200 workflow IDs. One that isn’t a UUID fails on its own.
- `op` · Required · string: `activate`, `pause`, `delete`, `addTags` or `move`.
- `tags` · Optional · array of strings: With `addTags`: at least one tag to add.
- `folderId` · Optional · UUID or null: With `move`: the folder, or `null` for none.

## What each op does

- `activate`: Publishes each, with the same checks as Publish a workflow. One that fails names its `step`.
- `pause`: Takes each live workflow offline. A workflow that isn’t live fails that ID.
- `delete`: Deletes each for good, with its runs.
- `addTags`: Adds the tags to each one’s own, up to 50 in all.
- `move`: Files each in `folderId`. A folder in another workspace fails that ID.

It takes no `Idempotency-Key`. Sending it again is safe for `activate`, `addTags` and `move`. A second `delete` or `pause` fails those IDs: they are gone, or no longer live.

## Responses

- 200 OK

  Each ID is in `succeeded` or in `failed`, with its reason. Read `failed`: a 200 doesn’t mean all worked.

- 400 `VALIDATION_ERROR`

  The body isn’t JSON, there are no IDs or more than 200, `op` is unknown, or `addTags` has no tag, over 50 tags, or a tag over 50 characters.

- 403 `FORBIDDEN`

  The token lacks **Write**, which every `op` needs, or the one its `op` adds: **Delete** for `delete`, **Publish** for `activate` and `pause`.

- 403 `FEATURE_OFF`

  A token made inside a partner’s product sent `move` while folders are switched off there.

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 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) · [`413 PAYLOAD_TOO_LARGE`](https://docs.gorunner.io/errors#PAYLOAD_TOO_LARGE) · [`415 INVALID_CONTENT_TYPE`](https://docs.gorunner.io/errors#INVALID_CONTENT_TYPE) · [`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 Write for workflows

curl -X POST "$GORUNNER/api/workflows/bulk" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "op": "addTags",
    "ids": [
      "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
      "6e9a1c3f-8b2d-4a57-9e0c-3f7b5d1a8c42"
    ],
    "tags": [
      "prod"
    ]
  }'
```

**JavaScript**

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

const res = await fetch(
  `${GORUNNER}/api/workflows/bulk`,
  {
    method: "POST",
    headers: {
        Authorization: `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
    body: JSON.stringify({
        op: "addTags",
        ids: [
          "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
          "6e9a1c3f-8b2d-4a57-9e0c-3f7b5d1a8c42",
        ],
        tags: ["prod"],
      }),
  },
);
const { data } = await res.json();
```

**Python**

```python
import os, requests

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

res = requests.post(
    f"{GORUNNER}/api/workflows/bulk",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
      "op": "addTags",
      "ids": [
        "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
        "6e9a1c3f-8b2d-4a57-9e0c-3f7b5d1a8c42",
      ],
      "tags": ["prod"],
    },
)
data = res.json()["data"]
```

Response · 200 OK

```json
{
  "data": {
    "op": "addTags",
    "succeeded": [
      "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31"
    ],
    "failed": [
      {
        "id": "6e9a1c3f-8b2d-4a57-9e0c-3f7b5d1a8c42",
        "error": "workflow not found"
      }
    ]
  }
}
```

**Every field**

### Body

- `ids` · Required · array of UUIDs: Workflow ids. An entry that is not a UUID is reported in `failed`, not rejected outright.
- `op` · Required · string: One of `activate`, `pause`, `delete`, `addTags`, `move`.
- `tags` · Optional · array of strings: Required (and only read) when `op` is `addTags`. Must contain at least one non-blank tag or the whole request is a 400, and so must at most 50 distinct tags of at most 50 characters each.
- `folderId` · Optional · UUID or null: Only read when `op` is `move`. `null` moves the workflows out of any folder. A folder from another workspace fails per id.

### Response · 200 OK

- `data` · object: Per-id outcome of a bulk operation. A 200 says the request was well formed, NOT that every workflow succeeded — always inspect `failed`.
  - `op` · string: One of `activate`, `pause`, `delete`, `addTags`, `move`.
  - `succeeded` · array of UUIDs: Always present, possibly empty.
  - `failed` · array of objects: Always present, possibly empty. One entry per id that did not apply, each with its own reason.
    - `id` · string: The id as sent — not necessarily a valid UUID.
    - `error` · string: Why this one failed, e.g. "Workflow not found.", "too many tags (max 50)", or a publish refusal’s sentence.
    - `step` · string: An `activate` refused for one step: that step’s display name.

Related: [Publish a workflow](https://docs.gorunner.io/api/publish/publish-workflow) · [Set a workflow’s tags](https://docs.gorunner.io/api/workflows/replace-workflow-tags) · [Delete a workflow](https://docs.gorunner.io/api/workflows/delete-workflow)

Source: https://docs.gorunner.io/api/workflows/bulk-update-workflows
