# Replace variables

**PUT** `/api/workflows/{id}/variables`

Sets a workflow’s variables. What you send is the whole set: any you leave out is deleted.

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

## Parameters

### Path

- `id` · Required · UUID: The workflow’s ID.

### Body

- `variables` · Required · array of objects: Every variable the workflow should have. Required: `[]` deletes them all.
- `variables.name` · Required · string: Letters, digits and underscores, not starting with a digit, up to 128 characters. Each name once.
- `variables.value` · Optional · string: Its value. A sensitive one needs 6 bytes or more, up to 8 KB, unless it has `keep`. A plain one without it is empty.
- `variables.isSecret` · Optional · boolean: `true` for sensitive.
- `variables.keep` · Optional · boolean: With `isSecret`: keep the stored value, which you can’t read back. Send no `value`.
- `expectedRev` · Optional · integer: The `definitionRev` you read. If the workflow changed since, nothing changes and you get 409.

## Keep a secret you can’t read

Read the variables, change the list, and send it all back. For a sensitive variable you aren’t changing, send `{"name": …, "isSecret": true, "keep": true}` with no value.

A sensitive value is masked in run data wherever it appears. That’s why a short one is refused: it would blank out unrelated text.

Plain variables live in the draft, so they reach live runs when you publish. Sensitive ones apply at once, so a leaked key can be replaced without publishing.

## Responses

- 200 OK

  The variables as GET answers them, without sensitive values, and the new `definitionRev`.

- 400 `VALIDATION_ERROR`

  `id` isn’t a UUID, the body isn’t JSON, it has no `variables` list (`details.field` says so, and nothing changes), or `expectedRev` isn’t a whole number.

- 403 `FORBIDDEN`

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

- 404 `NOT_FOUND`

  No workflow with this ID in the token’s workspace.

- 409 `CONFLICT`

  The workflow changed since your `expectedRev`, or another write took too long. Read it again first.

- 422 `VALIDATION_ERROR`

  A name isn’t allowed or is listed twice; a sensitive value is missing, under 6 bytes or over 8 KB; `keep` has nothing stored to keep; over 100 sensitive variables; or no encryption key on the server. Nothing changed.

- 500 `INTERNAL_ERROR`

  Nothing was changed. 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) · [`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
WORKFLOW_ID="your-workflow-id"

URL="$GORUNNER/api/workflows/$WORKFLOW_ID/variables"
curl -X PUT "$URL" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "variables": [
      {
        "name": "SLACK_CHANNEL",
        "value": "#alerts",
        "isSecret": false
      },
      {
        "name": "STRIPE_KEY",
        "isSecret": true,
        "keep": true
      }
    ],
    "expectedRev": 7
  }'
```

**JavaScript**

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

const res = await fetch(
  `${GORUNNER}/api/workflows/${WORKFLOW_ID}/` +
    `variables`,
  {
    method: "PUT",
    headers: {
        Authorization: `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
    body: JSON.stringify({
        variables: [
          {
            name: "SLACK_CHANNEL",
            value: "#alerts",
            isSecret: false,
          },
          {
            name: "STRIPE_KEY",
            isSecret: true,
            keep: true,
          },
        ],
        expectedRev: 7,
      }),
  },
);
const { data } = await res.json();
```

**Python**

```python
import os, requests

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

res = requests.put(
    f"{GORUNNER}/api/workflows/{WORKFLOW_ID}/"
    f"variables",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
      "variables": [
        {
          "name": "SLACK_CHANNEL",
          "value": "#alerts",
          "isSecret": False,
        },
        {
          "name": "STRIPE_KEY",
          "isSecret": True,
          "keep": True,
        },
      ],
      "expectedRev": 7,
    },
)
data = res.json()["data"]
```

Response · 200 OK

```json
{
  "data": {
    "variables": [
      {
        "name": "STRIPE_KEY",
        "value": "",
        "isSecret": true
      },
      {
        "name": "SLACK_CHANNEL",
        "value": "#alerts",
        "isSecret": false
      }
    ],
    "secretsAvailable": true,
    "definitionRev": 8,
    "hasUnpublishedChanges": true
  }
}
```

**Every field**

### Body

- `variables` · Required · array of objects
  - `name` · Required · string: Trimmed. Letters, digits and underscores, not starting with a digit, at most 128 characters. Duplicates in one request are refused.
  - `value` · Optional · string: Required on a sensitive entry unless `keep` is true; a plain entry without it holds an empty string. For `isSecret`, at least 6 characters (bytes) and at most 8,192 bytes — a shorter secret is refused rather than stored unprotected, because run-data masking matches the VALUE and would blank out unrelated text.
  - `isSecret` · Optional · boolean: Default `false`.
  - `keep` · Optional · boolean: Sensitive entries only (ignored on a plain one): retain the stored value, since the client was never given it and cannot send it back. Refused with 422 if there is nothing stored under that name. Default `false`.
- `expectedRev` · Optional · integer: Optimistic lock, as on `PUT /api/workflows/{id}`: the `definitionRev` you last read. If the workflow has been written since, the request is refused with 409 `CONFLICT` and nothing is changed. Omit it (or send 0) for an unconditional write.

### Response · 200 OK

- `data` · object
  - `variables` · array of objects: Sensitive variables first, then by name.
    - `name` · string
    - `value` · string: The value for a PLAIN variable. Always an empty string for a sensitive one — there is no read path for a secret.
    - `isSecret` · boolean: True = stored encrypted outside the definition and takes effect immediately, without a publish. False = lives in the draft definition and reaches production only when you publish.
  - `secretsAvailable` · boolean: False when the server has no encryption key configured — writing any `isSecret` variable will then be refused with 422.
  - `definitionRev` · integer: The workflow’s revision as this answer leaves it. Plain variables live in the draft definition, so a `PUT` moves it: send this as `expectedRev` on your next write to the workflow (`PUT /api/workflows/{id}` or this endpoint).
  - `hasUnpublishedChanges` · boolean: Whether the draft is now ahead of the published version — a variable change on a live workflow takes effect only when published.

Related: [List variables](https://docs.gorunner.io/api/publish/get-workflow-variables) · [Variables](https://docs.gorunner.io/build/variables)

Source: https://docs.gorunner.io/api/publish/replace-workflow-variables
