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

# Supply an Agent's Secrets

> Save encrypted workspace secrets once and let each launch resolve them by name.

**Before You Begin:** A BoxRouter API key with `credentials:manage` and `sandbox:execute`, and an [SDK](https://boxrouter.ai/docs/sdk.md) or the [CLI](https://boxrouter.ai/docs/cli.md#install). The CLI launch needs an interactive terminal.

A manifest lists secret names; values live in your workspace and reach the sandbox as environment
variables. The agent and every command it runs can read them. Put non-secret settings in the
manifest's `env` instead. Keep secrets out of names, labels, idempotency keys, and support reports.

A name starts with an uppercase letter and holds uppercase letters, digits and underscores, up to 128
characters (`^[A-Z][A-Z0-9_]{0,127}$`). A value is at most 16,384 characters.

## Save a Secret

Set `MODEL_API_KEY` in your environment, then pipe it in or read it from the environment in code. You can also run `boxrouter secrets set
MODEL_API_KEY` in a terminal and type the value at a hidden prompt. These examples do not put the
value in shell history or process arguments. Setting an existing name replaces its value.

```python Python theme={"theme":"css-variables"}
import json
import os

from boxrouter import BoxRouter
from boxrouter.models import PutSecret, SecretList

with BoxRouter() as client:
    client.api.secrets.put("MODEL_API_KEY", body=PutSecret(value=os.environ["MODEL_API_KEY"]))
    secrets = client.api.secrets.list()
    if not isinstance(secrets, SecretList):
        raise RuntimeError("The secret list was not returned")
    for secret in secrets.secrets:
        print(json.dumps(secret.to_dict()))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const value = process.env.MODEL_API_KEY;
if (!value) throw new Error('Set MODEL_API_KEY in your environment');

const client = new BoxRouter();
try {
  await client.api.secrets.put({ path: { name: 'MODEL_API_KEY' }, body: { value } });
  const { secrets } = await client.api.secrets.list();
  for (const secret of secrets) console.log(JSON.stringify(secret));
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
printf '%s' "$MODEL_API_KEY" | boxrouter secrets set MODEL_API_KEY
boxrouter secrets ls
```

Output

```text theme={"theme":"css-variables"}
MODEL_API_KEY
MODEL_API_KEY	2026-10-29T10:53:38.692095Z	2026-10-29T10:53:38.692095Z
```

The CLI output above is `set` printing the name, then `ls` printing a row; the SDK examples print each listing as JSON.
Listing shows names and timestamps, never values. These
calls need a key with `credentials:manage`, which is separate from `provider-accounts:manage` (connected
providers). A key that only runs sandboxes can't list, save, or delete secrets.

## Use It in a Launch

Declare the name in a manifest. This command checks whether the secret arrived without printing its
value. Save the manifest as `boxrouter.yaml`:

```yaml title="boxrouter.yaml" theme={"theme":"css-variables"}
name: secret-check
interactive: true
secrets: [MODEL_API_KEY]
command: 'if [ -n "$MODEL_API_KEY" ]; then echo "Secret is set"; exit 0; else echo "Secret is missing"; exit 1; fi'
```

Launch it from an interactive terminal. `--rm` requests sandbox deletion when the command ends. The SDK examples pass the same manifest as an object instead of loading the file. They print the
terminal's output, which can include the shell prompt and the echoed command.

```python Python theme={"theme":"css-variables"}
import sys

from boxrouter import BoxRouter
from boxrouter.models import AgentManifest

manifest = AgentManifest(
    name="secret-check",
    interactive=True,
    secrets=["MODEL_API_KEY"],
    command='if [ -n "$MODEL_API_KEY" ]; then echo "Secret is set"; exit 0; else echo "Secret is missing"; exit 1; fi',
)

with BoxRouter() as client:
    with client.sandboxes.launch(manifest=manifest) as box:
        terminal = box.open_terminal()
        for chunk in terminal.output():
            sys.stdout.buffer.write(chunk)
        sys.stdout.buffer.flush()
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

await using client = new BoxRouter();
const command = 'if [ -n "$MODEL_API_KEY" ]; then echo "Secret is set"; exit 0; else echo "Secret is missing"; exit 1; fi';
const manifest = { name: 'secret-check', interactive: true, secrets: ['MODEL_API_KEY'], command };
await using box = await client.sandboxes.launch({ manifest });
const terminal = await box.openTerminal();
for await (const chunk of terminal.output()) process.stdout.write(chunk);
```

```bash CLI theme={"theme":"css-variables"}
boxrouter launch --manifest boxrouter.yaml --rm
```

Output

```text theme={"theme":"css-variables"}
Secret is set
```

For catalog agents, you can select a saved secret with `--secret NAME`. See
[Give an agent model access](https://boxrouter.ai/docs/launch/agents.md#give-an-agent-model-access) for the names each agent reads.

## When a Name Isn't Saved

A launch that declares a name you haven't saved fails with `secret_missing` and
`details.names` listing what is missing:

```text theme={"theme":"css-variables"}
One or more named workspace secrets are missing.
Fix: Supply the missing secrets (NOT_SAVED_NAME) or add them under Secrets in the dashboard or with `boxrouter secrets set NAME`, then retry.
```

How a missing name is filled depends on the launch:

| Launch | What Happens |
| - | - |
| `boxrouter launch` of one agent | The CLI retries with an environment variable of that name, then a hidden prompt. If neither is available, the launch fails. |
| `boxrouter launch --copies N` | No fallback. The CLI tells you to run `boxrouter secrets set NAME` and retry. |
| API request | Send `secret_values` for that one launch. |

A fallback or `secret_values` value is used for that launch only. It isn't saved, wins over a saved
secret with the same name, and is never returned by BoxRouter. A `--copies` run refuses `secret_values`.

A [run of copies](https://boxrouter.ai/docs/launch/copies.md) reads each saved secret by name as each copy starts. If you rotate a value,
only the copies that start afterward get the new one.

With `--repo`, the CLI can't see the remote manifest's names before it submits. Save them first, or
launch from a local manifest that declares them.

## Delete a Secret

```python Python theme={"theme":"css-variables"}
from boxrouter import BoxRouter

with BoxRouter() as client:
    client.api.secrets.delete("MODEL_API_KEY")
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  await client.api.secrets.delete({ path: { name: 'MODEL_API_KEY' } });
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter secrets rm MODEL_API_KEY
```

Deleting removes only the saved value. The key stays valid at its service, and sandboxes that already
received it keep it. To revoke it, rotate the key at its service and destroy those sandboxes.

## How Secrets Are Kept

Secrets are scoped to your workspace and stored encrypted, and no read returns a value. Deleting one does not
immediately purge it from recovery copies; [Data retention](https://boxrouter.ai/docs/data-retention.md) lists what is kept and for how long.
Your BoxRouter API key and [provider credentials](https://boxrouter.ai/docs/provider-keys.md) are separate from workspace secrets.

## Next Steps

> [card] [Launch Your Own Agent](https://boxrouter.ai/docs/launch/your-own-agent.md)
>
> Launch a repository, manifest, or image.

> [card] [Configure a Launch Manifest](https://boxrouter.ai/docs/launch/manifest.md)
>
> Declare secret names in a manifest.

> [card] [Choose a Catalog Agent](https://boxrouter.ai/docs/launch/agents.md)
>
> List the agents your workspace can launch.


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