> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eigenpal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Variables and secrets

> Keep URLs, IDs and secrets out of workflow definitions, with a value per environment.

A **variable** is a named value a workflow reads as `{{ env.NAME }}`: an API base
URL, a tenant ID in a downstream system, an auth token. Define it once under
**Variables** in the sidebar and use it in any step field, condition, output or
script input.

These are workflow variables, managed in the app. The process environment of a
self-hosted installation is configured separately; see
[Environment variables](https://docs.eigenpal.com/self-hosting/environment-variables.md).

```yaml theme={null}
steps:
  - name: push_to_crm
    type: action.http
    with:
      url: '{{ env.CRM_API_URL }}/invoices'
      headers:
        Authorization: 'Bearer {{ env.CRM_TOKEN }}'
```

## Plain and secret

| Kind | Who can read the value | In run records |
| - | - | - |
| Plain | Anyone who can see workflows | Stored as is |
| Secret | Nobody, after it is saved; replace it only | Masked `[secret:NAME]` |

Secrets are encrypted with your organization's key. The API never returns them.
Run inputs, resolved step configuration, outputs, errors and the run's failure
are stored with each secret value replaced by `[secret:NAME]`; a retry of a step
still sends the real value to where that step uses it.

* Masking covers the exact value: a step that transforms a secret (encodes it,
  cuts it) stores the transformed text. Values shorter than 8 characters are
  not masked.
* Keep secrets in headers, URLs and credentials. The editor warns when an AI
  step's prompt uses one: prompts are kept in the run trace as sent.
* Reference a secret where it is used. A step output that contains one is
  stored masked, so steps after a pause (a human review, say) read
  `[secret:NAME]` from it. A child workflow (`execution: child`) cannot receive
  a secret as input; it references the variable itself.
* Anyone who can edit a workflow can use any organization secret in it. Put a
  secret on one workflow when only that workflow should have it.

## Types

Each variable is **text**, a **number** or **true/false**, and its value is checked
when you save it. Templates render all three as text. A bare `{{ env.MAX_PAGES }}`
in a script input or condition arrives as its type, so the script editor types
that parameter as `number`. Secrets are always text.

## Organization and workflow variables

* **Organization variables** apply to every workflow. Owners and admins manage them.
* **Workflow variables** apply to the workflows you choose for them, one or
  several, and win over an organization variable of the same name there. Choose
  several workflows to share one value without copying it between them.
  Changing one needs edit access to every workflow it applies to, since its
  value reaches all of them. Manage them on the Variables page or from a
  workflow's **Variables** panel (in the editor's ⋯ menu).

A workflow can have only one workflow variable of a given name. A workflow
variable that turns out to be useful everywhere can be moved to the
organization from its menu by an owner or admin; it keeps its value. Deleting a
workflow takes it off its variables, and deletes those it was the last workflow
of.

Workflows called inline (`action.invoke-workflow` with `execution: inline`) run
inside the calling run: a name only the called workflow uses resolves with its
own variables first.

## What happens when one is missing

EigenPal reads the variables a workflow uses straight from its definition.

* The editor marks each step that uses an undefined variable, and the toolbar
  shows how many are missing. Define them from the Variables panel.
* The Variables page lists every variable a workflow uses but nobody defined.
* A run that needs an undefined variable fails before its first step, with an
  error that names it. It never runs with an empty value.

Write names out in full and in capitals: `env.CRM_TOKEN` or
`env['CRM_TOKEN']`. A name built at run time (`env[some_name]`) is not
supported, and the editor flags lowercase names, which never resolve.

A run resolves its variables when it starts and again when it resumes, so a
changed value applies to the steps that have not run yet.

## Across environments

With [Git-based environment management](https://docs.eigenpal.com/self-hosting/workflow-promotion.md), every environment
(Dev, SIT, Prod) keeps its own values for the same names. Values never leave an
environment.

* A released version lists the variables it uses, by name and type.
* Each environment reports which variables it defines, by name and type.
* **Release** refuses to start into an environment that lacks a variable, or
  defines it with another type, and says where to define it.
* An environment that receives a version it cannot run yet holds it, and takes
  it on its next pull once the variable is defined there.
* On the Variables page, each row shows whether the later environments define
  it, so you can fill in Prod before the release gets there, and each
  environment lists what the releases waiting in it need.

A workflow variable can be defined in an environment before the workflow's
first release reaches it, so workflow-specific values never have to become
organization-wide.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.