# Get a workflow variable

> GET /v1/workflow-variables/{id} · Workflows API

Source: https://developer.kustomer.com/api/workflows/get-a-workflow-variable

```http
GET https://{orgname}.api.kustomerapp.com/v1/workflow-variables/{id}
Authorization: Bearer <API key>
```

## Authorization

Requires any one of these roles: `org.permission.workflow`; or legacy `org.admin.workflow`. The API key or user must hold at least one of them. Legacy roles predate permission sets and still work.

Retrieves a workflow variable based on the Workflow Variable ID.

### Note
> `maskedValue` attribute represents the last 6 characters of the value of the variable.

## About Workflow variables

A workflow variable is a named value stored with one workflow, such as an API key or a URL that differs between sandbox and production. Steps can use it without the value being written into the workflow itself.

Anything written directly into a [workflow's](https://developer.kustomer.com/api/workflows#workflows) params is visible to everyone who can view workflows and is returned by `GET /v1/workflows`. Variables keep secrets and environment-specific values out of the document. Each variable belongs to exactly one workflow, so two workflows that need the same token each hold their own copy. A workflow whose trigger or steps reference a variable reports `containsVariables: true`.

Steps read a variable by its **ID**, not its name: `/#variables.<variableId>` in a param, or `{{{variables.<variableId>}}}` inside a template, for example in an `Authorization` header of a REST step. The ID is the 24-character `id` returned when you create the variable; a reference that uses the name resolves to nothing. Variables are removed from the run context that failed-run records store, so their values don't leak into error logs.

#### Values are write-only

| Attribute | Meaning |
| --- | --- |
| `name` | A label for people; steps refer to the variable by its `id`. Letters, numbers, `-`, `_` and `.`, up to 128 characters. |
| `value` | The secret, 1 to 1,024 characters. Accepted on create and update, never returned. |
| `maskedValue` | The last six characters of the value, so you can tell which key is stored. |
| `type` | Always `string`. |

Because the value can't be read back, keep the source of truth in your own secret store and write it to Kustomer when it changes. Workflows copied to another workflow or org don't bring their variables with them: create them again on the copy, then update every variable reference to the new IDs.

#### Which endpoint to use

| To | Call |
| --- | --- |
| List a workflow's variables | [`GET /v1/workflows/{id}/variables`](https://developer.kustomer.com/api/workflows/get-all-variables-for-a-workflow) |
| Add a variable to a workflow | [`POST /v1/workflows/{id}/variables`](https://developer.kustomer.com/api/workflows/create-a-workflow-variable-for-a-workflow) |
| Read, change or delete one variable | [`GET`](https://developer.kustomer.com/api/workflows/get-a-workflow-variable), [`PUT`](https://developer.kustomer.com/api/workflows/update-a-workflow-variable) or [`DELETE /v1/workflow-variables/{id}`](https://developer.kustomer.com/api/workflows/delete-a-workflow-variable) |

Creating a variable needs `name`, `value` and `type`; `PUT` changes only the fields you send, so you can rotate a secret by sending just `value`. Variables are cached, so a new or changed value can take about a minute to reach new runs, just like workflow edits. When you create a workflow that depends on variables, create it disabled, add the variables, then enable it, so no run starts without them.

Related: [Workflows](https://developer.kustomer.com/api/workflows#workflows), [Conditions and variables](https://developer.kustomer.com/docs/workflows/conditions-and-expressions), [Call your own code](https://developer.kustomer.com/docs/workflows/code-steps)

### Recipes

#### 1. Give a REST step an API key without writing it into the workflow

Store a token for your own service as a variable and send it in a REST step's header, so it never appears in the workflow document.

**Step 1.** [`POST /v1/workflows/{id}/variables`](https://developer.kustomer.com/api/workflows/create-a-workflow-variable-for-a-workflow.md). Create the variable on the workflow that needs it. `name`, `value` and `type` (always `string`) are all required. Keep the `id` from the response: steps refer to the variable by that ID, and a reference that uses the name resolves to nothing.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/workflows/$WORKFLOW_ID/variables" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"name\": \"warehouseToken\", \"value\": \"$WAREHOUSE_TOKEN\", \"type\": \"string\"}"
```

```bash title="kusty"
VARIABLE_ID=$(kusty api POST /v1/workflows/$WORKFLOW_ID/variables \
  --field name=warehouseToken --field value="$WAREHOUSE_TOKEN" --field type=string \
  | jq -r '.data.id')
```

**Step 2.** [`GET /v1/workflows/{id}`](https://developer.kustomer.com/api/workflows/get-a-workflow-based-on-the-id.md). Read the workflow and set the REST step's header to `Bearer {{{variables.<variableId>}}}`. Triple braces keep the token from being HTML-escaped. Remove the `_id` Kustomer adds to each step before you send the steps back.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/workflows/$WORKFLOW_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/workflows/$WORKFLOW_ID \
  | jq --arg step "$STEP_ID" --arg var "$VARIABLE_ID" '.data.attributes
      | {trigger, steps: [.steps[] | del(._id)
          | if .id == $step then .params.headers.Authorization = "Bearer {{{variables.\($var)}}}" else . end]}' > update.json
```

**Step 3.** [`PUT /v1/workflows/{id}`](https://developer.kustomer.com/api/workflows/update-a-workflow-based-on-the-id.md). Send `trigger` and `steps` together; `steps` replaces the whole stored array. The workflow now reports `containsVariables: true`, and the token's value stays out of the document and out of failed-run records.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/workflows/$WORKFLOW_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data @update.json
```

```bash title="kusty"
kusty api PUT /v1/workflows/$WORKFLOW_ID --data @update.json --fields attributes.containsVariables
```

> See [Call your own code](https://developer.kustomer.com/docs/workflows/code-steps) for the REST step's other params and how to sign requests.

#### 2. Rotate a secret in every workflow that uses it

Replace an expiring API token everywhere at once, for example from the job that rotates it in your secret store.

**Step 1.** [`GET /v1/workflows`](https://developer.kustomer.com/api/workflows/get-workflows.md). List the workflows that reference any variable. `containsVariables` is `true` only when a workflow's trigger or steps use one, so this skips the rest.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/workflows?pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/workflows --all \
  | jq -r 'select(.data.attributes.containsVariables) | .data.id' > workflows.txt
```

**Step 2.** [`GET /v1/workflows/{id}/variables`](https://developer.kustomer.com/api/workflows/get-all-variables-for-a-workflow.md). Each workflow holds its own copy of a variable, so look for the name in each one. Names aren't required to be unique, so one workflow can return more than one match.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/workflows/$WORKFLOW_ID/variables?pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
while read -r wf; do
  kusty api GET /v1/workflows/$wf/variables --all \
    | jq -r 'select(.data.attributes.name == "warehouseToken") | .data.id'
done < workflows.txt > variables.txt
```

**Step 3.** [`PUT /v1/workflow-variables/{id}`](https://developer.kustomer.com/api/workflows/update-a-workflow-variable.md). Send only the new `value`; the name and ID stay the same, so no workflow needs editing. The response's `maskedValue` is the last six characters of what you stored, which is enough to confirm the change. New runs use the new value within about a minute.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/workflow-variables/$VARIABLE_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"value\": \"$NEW_TOKEN\"}"
```

```bash title="kusty"
while read -r id; do
  kusty api PUT /v1/workflow-variables/$id --field value="$NEW_TOKEN" --fields attributes.maskedValue
done < variables.txt
```

> Keep the old token valid for a minute or two after the update, so runs that already started don't fail.

#### 3. Recreate variables on a copied workflow

After copying a workflow to another org or into a new workflow, give the copy its own variables and point its steps at them.

**Step 1.** [`GET /v1/workflows/{id}/variables`](https://developer.kustomer.com/api/workflows/get-all-variables-for-a-workflow.md). List the original workflow's variables for their names and IDs. Values are never returned, so take them from your own secret store.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/workflows/$SOURCE_WORKFLOW_ID/variables?pageSize=100" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/workflows/$SOURCE_WORKFLOW_ID/variables --all \
  | jq -r '[.data.id, .data.attributes.name] | @tsv'
```

**Step 2.** [`POST /v1/workflows/{id}/variables`](https://developer.kustomer.com/api/workflows/create-a-workflow-variable-for-a-workflow.md). Create each variable on the copy and note which new ID replaces which old one.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/workflows/$COPY_WORKFLOW_ID/variables" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"name\": \"$NAME\", \"value\": \"$VALUE\", \"type\": \"string\"}"
```

```bash title="kusty"
NEW_VARIABLE_ID=$(kusty api POST /v1/workflows/$COPY_WORKFLOW_ID/variables \
  --field name="$NAME" --field value="$VALUE" --field type=string | jq -r '.data.id')
```

**Step 3.** [`PUT /v1/workflows/{id}`](https://developer.kustomer.com/api/workflows/update-a-workflow-based-on-the-id.md). Replace every old ID with its new one in the copy's `trigger` and `steps`, then send both. Until you do, the copy's references resolve to nothing, because a workflow can only read its own variables.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/workflows/$COPY_WORKFLOW_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data @update.json
```

```bash title="kusty"
kusty api GET /v1/workflows/$COPY_WORKFLOW_ID \
  | jq --arg old "$OLD_VARIABLE_ID" --arg new "$NEW_VARIABLE_ID" '.data.attributes
      | {trigger, steps: [.steps[] | del(._id)]} | tojson | gsub($old; $new) | fromjson' > update.json
kusty api PUT /v1/workflows/$COPY_WORKFLOW_ID --data @update.json
```

#### 4. Delete a variable that nothing uses

Clean up old secrets safely by checking that the workflow no longer references a variable before deleting it.

**Step 1.** [`GET /v1/workflow-variables/{id}`](https://developer.kustomer.com/api/workflows/get-a-workflow-variable.md). Read the variable to find the workflow it belongs to, in `relationships.workflow`.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/workflow-variables/$VARIABLE_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
WORKFLOW_ID=$(kusty api GET /v1/workflow-variables/$VARIABLE_ID | jq -r '.data.relationships.workflow.data.id')
```

**Step 2.** [`GET /v1/workflows/{id}`](https://developer.kustomer.com/api/workflows/get-a-workflow-based-on-the-id.md). Search the workflow's `trigger` and `steps` for the variable's ID. If it's there, a step still uses it, and deleting it would make those references resolve to nothing.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/workflows/$WORKFLOW_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/workflows/$WORKFLOW_ID \
  | jq --arg id "$VARIABLE_ID" '.data.attributes | {trigger, steps} | tojson | contains($id)'
```

**Step 3.** [`DELETE /v1/workflow-variables/{id}`](https://developer.kustomer.com/api/workflows/delete-a-workflow-variable.md). If the check printed `false`, delete it. The response is `204` with no body.

```bash title="cURL"
curl --request DELETE "https://api.kustomerapp.com/v1/workflow-variables/$VARIABLE_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api DELETE /v1/workflow-variables/$VARIABLE_ID --confirm --yes
```

### Example object

A real record from [`GET /v1/workflow-variables/{id}`](https://developer.kustomer.com/api/workflows/get-a-workflow-variable.md):

```json
{
  "id": "55d7998f2d3f9f8800099aed",
  "type": "variable",
  "attributes": {
    "maskedValue": "cdefgh",
    "name": "apiKey1",
    "type": "string",
    "createdAt": "2019-09-23T12:14:00Z",
    "updatedAt": "2019-09-23T12:14:00Z",
    "modifiedAt": "2019-09-23T12:14:00Z"
  },
  "relationships": {
    "org": {
      "data": {
        "type": "org",
        "id": "55d7998f2d3f9f8800099aec"
      },
      "links": {
        "self": "/v1/orgs/55d7998f2d3f9f8800099aec"
      }
    },
    "createdBy": {
      "data": {
        "type": "user",
        "id": "55d7998f2d3f9f8800099abc"
      },
      "links": {
        "self": "/v1/users/55d7998f2d3f9f8800099abc"
      }
    },
    "modifiedBy": {
      "data": {
        "type": "user",
        "id": "55d7998f2d3f9f8800099abd"
      },
      "links": {
        "self": "/v1/users/55d7998f2d3f9f8800099abd"
      }
    },
    "workflow": {
      "data": {
        "type": "workflow",
        "id": "5b84680f6a3d70001b4c7183"
      },
      "links": {
        "self": "/v1/workflows/5b84680f6a3d70001b4c7183"
      }
    }
  },
  "links": {
    "self": "/v1/workflow-variables/55d7998f2d3f9f8800099aed"
  }
}
```

Fields:

- `id` (string): What steps use to read the variable: `/#variables.<id>` as a whole param, or `{{{variables.<id>}}}` inside a template.
- `attributes.maskedValue` (string): The last six characters of the value, so you can tell which secret is stored. The full value is never returned.
- `attributes.name` (string): A label for people, not the way steps refer to the variable. Letters, numbers, `-`, `_` and `.` (no spaces), up to 128 characters. Names don't have to be unique.
- `attributes.type` (string): Always `string`, and required when you create a variable.
- `relationships.workflow` (object): The one workflow whose steps can read this variable. Variables can't be shared or moved; create a copy on each workflow that needs one.

### Relationships

- Points to **Workflows** via `relationships.workflow`: Each workflow variable belongs to exactly one workflow, and only that workflow's steps can read it. A workflow can have many variables.

### Permissions

Fewest roles for a read-only integration: `org.permission.workflow`, `org.permission.workflow.read`. For every endpoint: `org.permission.workflow`, `org.permission.workflow.create`, `org.permission.workflow.read`.

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Workflow Variable ID |

## Responses

### 200

OK

- `data` (object, required)
  - `id` (string, required)
  - `type` (string, required)
  - `attributes` (object, required)
    - `maskedValue` (string, required)
    - `name` (string, required)
    - `type` (string, required)
    - `createdAt` (string, required)
    - `updatedAt` (string, required)
    - `modifiedAt` (string, required)
  - `relationships` (object, required)
    - `org` (object, required)
    - `createdBy` (object, required)
    - `modifiedBy` (object, required)
    - `workflow` (object, required)
  - `links` (object, required)
    - `self` (string, required)

```json
{
  "data": {
    "id": "55d7998f2d3f9f8800099aed",
    "type": "variable",
    "attributes": {
      "maskedValue": "cdefgh",
      "name": "apiKey1",
      "type": "string",
      "createdAt": "2019-09-23T12:14:00Z",
      "updatedAt": "2019-09-23T12:14:00Z",
      "modifiedAt": "2019-09-23T12:14:00Z"
    },
    "relationships": {
      "org": {
        "data": {
          "type": "org",
          "id": "55d7998f2d3f9f8800099aec"
        },
        "links": {
          "self": "/v1/orgs/55d7998f2d3f9f8800099aec"
        }
      },
      "createdBy": {
        "data": {
          "type": "user",
          "id": "55d7998f2d3f9f8800099abc"
        },
        "links": {
          "self": "/v1/users/55d7998f2d3f9f8800099abc"
        }
      },
      "modifiedBy": {
        "data": {
          "type": "user",
          "id": "55d7998f2d3f9f8800099abd"
        },
        "links": {
          "self": "/v1/users/55d7998f2d3f9f8800099abd"
        }
      },
      "workflow": {
        "data": {
          "type": "workflow",
          "id": "5b84680f6a3d70001b4c7183"
        },
        "links": {
          "self": "/v1/workflows/5b84680f6a3d70001b4c7183"
        }
      }
    },
    "links": {
      "self": "/v1/workflow-variables/55d7998f2d3f9f8800099aed"
    }
  }
}
```

### 401

Unauthorized. Missing or invalid bearer token.

### 403

Forbidden. The authenticated user does not have permission to view workflow variables.

### 404

Not Found. No workflow variable found with the given ID.

## Example request

```bash
curl --request GET \
  --url 'https://api.kustomerapp.com/v1/workflow-variables/5b84680f6a3d70001b4c7183' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```
