# List variables

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

Lists a workflow’s variables. Sensitive ones come back by name only.

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

## Parameters

### Path

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

## Two kinds

| What | Plain | Sensitive |
| --- | --- | --- |
| Where it is kept | In the draft | Encrypted, outside the draft |
| Read back | With its `value` | Never: `value` is `""` |
| Takes effect | When you publish | At once |
| In exports and templates | Yes | No |
| In a duplicate | Yes | Yes |

Steps read both as `{{$vars.NAME}}`. `secretsAvailable` is `false` when the server has no encryption key: sensitive variables can’t be saved there.

## Responses

- 200 OK

  Every variable, sensitive ones first, then by name.

- 400 `VALIDATION_ERROR`

  `id` isn’t a UUID.

- 403 `FORBIDDEN`

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

- 404 `NOT_FOUND`

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

- 500 `INTERNAL_ERROR`

  The sensitive variables 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
WORKFLOW_ID="your-workflow-id"

curl "$GORUNNER/api/workflows/$WORKFLOW_ID/variables" \
  -H "Authorization: Bearer $GORUNNER_TOKEN"
```

**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`,
  {
    headers: { Authorization: `Bearer ${TOKEN}` },
  },
);
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.get(
    f"{GORUNNER}/api/workflows/{WORKFLOW_ID}/"
    f"variables",
    headers={"Authorization": f"Bearer {TOKEN}"},
)
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": 7,
    "hasUnpublishedChanges": false
  }
}
```

**Every field**

It takes no body.

### 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: [Replace variables](https://docs.gorunner.io/api/publish/replace-workflow-variables) · [Variables](https://docs.gorunner.io/build/variables)

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