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

# env

> Read environment variables in agent code, and reference them from workflow definitions

`env(key)` reads one environment variable as a string. Locally it resolves from your shell environment merged with the project's `.env` file; in a deployed agent it resolves from the [environment](/concepts/environments)'s variable store that you manage with [`lua env`](/reference/cli/env). Available everywhere agent code runs, including workflow code steps, where `ctx.env[key]` reads the same values; `process.env.KEY` works too.

*Verified against lua-cli 3.33.0.*

```ts theme={null}
import { env } from 'lua-cli';
```

## Quick example

Treat a missing secret as a configuration error.

```ts theme={null}
import { env } from 'lua-cli';

const apiKey = env('WEATHER_API_KEY');
if (!apiKey) throw new Error('WEATHER_API_KEY is not set');
```

## Functions

### env(key, options?)

Returns the variable's value, or `undefined` when it isn't set.

```ts theme={null}
env(key: string, options?: { manifest?: boolean }): string | undefined
```

<ParamField path="key" type="string" required>
  The variable name, for example `WEATHER_API_KEY`.
</ParamField>

<ParamField path="options.manifest" type="boolean">
  Accepted for compatibility; the function ignores it.
</ParamField>

Where the value comes from:

| Run                       | Source                                                                                      |
| ------------------------- | ------------------------------------------------------------------------------------------- |
| `lua test`                | `process.env`, then `.env` in the project root; the file wins                               |
| Sandbox turn (`lua chat`) | The `.env` values uploaded with each sandbox version; there is no server-side sandbox store |
| Deployed, production      | The production store: `lua env production -k <KEY> -v <value>`                              |

Values are always strings; convert numbers and booleans yourself.

**Returns** — the value, or `undefined`.

**Example**

```ts theme={null}
import { LuaTool, env } from 'lua-cli';
import { z } from 'zod';

export default class GetWeatherTool implements LuaTool {
  name = 'get_weather';
  description = 'Current weather for a city';
  inputSchema = z.object({ city: z.string() });

  async execute(input: z.infer<typeof this.inputSchema>) {
    const apiKey = env('WEATHER_API_KEY');
    if (!apiKey) throw new Error('WEATHER_API_KEY is not set');
    const baseUrl = env('WEATHER_API_URL') ?? 'https://api.example.com';
    const response = await fetch(`${baseUrl}/v1/current?q=${encodeURIComponent(input.city)}`, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    return (await response.json()) as { temperature: number; condition: string };
  }
}
```

**Errors** — none; a missing variable returns `undefined`.

### env.template(key)

Returns a placeholder that a [workflow definition](/reference/sdk/workflow-builder) resolves from the target agent's environment at `lua push`, wherever a `template(...)` binding is accepted.

```ts theme={null}
env.template(key: string): { __envRef: string }
```

<ParamField path="key" type="string" required>
  The variable name. Names ending in `SECRET`, `TOKEN`, `KEY`, or `PASSWORD` are refused.
</ParamField>

**Returns** — `{ __envRef: key }`. It is never a value: read the variable with `env(key)` inside the step that needs it.

**Example**

```ts theme={null}
import { env } from 'lua-cli';

export const timezone = env.template('FINANCE_TZ');
```

**Errors** — `env-template-secret-key: \`API\_KEY\` looks like a secret — read it with env('API\_KEY') inside execute instead\` for a secret-looking name.

<Note>
  `env.template` belongs in workflow files, which the compiler evaluates. In a deployed agent the runtime `env` has no `template` member (`env.template is not a function`), and in `lua test` it returns the placeholder without the secret-name check.
</Note>

## Types

<ResponseField name="PersonaText" type="string | { base?: string; voice?: string; text?: string }">
  The shape of `persona` on [`LuaAgent`](/reference/sdk/luaagent). A string is the whole [persona](/concepts/persona); in object form `base` is always rendered, `voice` is appended on voice channels, and `text` on text channels.
</ResponseField>

## See also

* [`lua env`](/reference/cli/env) — set, list, and delete variables per environment
* [About environments](/concepts/environments) — sandbox and production, and what each shares
* [Call your API](/build/call-your-api) — keep credentials out of code with `env`
* [Workflow builder](/reference/sdk/workflow-builder) — where `env.template` bindings are accepted
