# Using Secrets with Deployed Workflows
Source: https://docs.chain.link/cre/guides/workflow/secrets/using-secrets-deployed
Last Updated: 2026-01-20


When your workflow is deployed, it cannot access your local `.env` file or environment variables. Instead, secrets must be stored in the **Vault DON**—a decentralized, secure secret storage system that your deployed workflows can access at runtime.

This guide explains how to manage secrets for deployed workflows using the `cre secrets` CLI commands.

> **NOTE: For local simulation**
>
> If you're developing and testing locally with `cre workflow simulate`, you don't need the Vault DON. See [Using Secrets in Simulation](/cre/guides/workflow/secrets/using-secrets-simulation) instead.

## Prerequisites

Before managing secrets for deployed workflows, ensure you have:

1. **CRE CLI installed**: See the [Installation Guide](/cre/getting-started/cli-installation/macos-linux)
2. **Authentication**: You must be logged in with `cre login`
3. **(Web3-keyed flow only) Owner address configured**: When managing secrets owned by a workflow owner address (the onchain registry flow), your `workflow-owner-address` must be set. Browser-auth flows on the private registry use your CRE login session and don't require this.

## Auth modes: web3 key vs. browser auth

`cre secrets` supports two ways to authorize Vault operations, mirroring the [private vs. public workflow management split](/cre/guides/operations/deploying-workflows#private-vs-public-workflow-management) used for workflow deployment:

- **Web3-keyed.** Each operation submits an onchain authorization tied to your linked workflow owner address (`workflow-owner-address`). Use this when your workflow is deployed to the onchain registry (`deployment-registry: "onchain:ethereum-mainnet"`).
- **Browser auth (`--secrets-auth=browser`).** The CLI opens a browser-based OAuth (PKCE) flow against the Vault DON gateway and authorizes the operation against your CRE organization. No wallet, no gas, no `workflow-owner-address` needed. Use this when your workflow is deployed to the `private` registry, or when your team manages secrets at the organization level rather than per-wallet.

You can specify the mode per command with `--secrets-auth`. If omitted, the CLI uses the default for your target.

```bash
# Web3-keyed (onchain registry)
cre secrets create production-secrets.yaml --target production-settings

# Browser auth (private registry / org-owned)
cre secrets create production-secrets.yaml --target staging-settings --secrets-auth=browser
```

## How secrets work with deployed workflows

The workflow is similar to local development, but with a critical difference in where secrets are stored:

1. **Declare**: Define secret identifiers in a YAML file
2. **Store**: Push secrets to the Vault DON using `cre secrets create`
3. **Use**: Your deployed workflow accesses secrets from the Vault using `runtime.GetSecret()`

**Key difference from simulation:**

- **Local simulation**: Secrets read from your environment variables or `.env` file on your machine
- **Deployed workflows**: Secrets retrieved from Vault DON by the workflow

## Step-by-step guide

### Step 1: Create a secrets YAML file

Create a YAML file at the root of your project that declares the secrets you want to store.

**Example `production-secrets.yaml`:**

```yaml
secretsNames:
  API_KEY:
    - API_KEY_VALUE

  DATABASE_URL:
    - DATABASE_URL_VALUE
```

**Structure:**

- `secretsNames` — Top-level key containing all secrets
- Each secret has:
  - **Key** (e.g., `API_KEY`) — The identifier your workflow code will use
  - **Value** — An array containing the environment variable name that holds the actual value

> **CAUTION: Don't commit secrets files**
>
> Add your production secrets YAML files to `.gitignore`. Never commit them to version control.

### Step 2: Provide secret values as environment variables

Set the actual secret values as environment variables. These can be provided in two ways:

**Option A: Export in your shell**

```bash
export API_KEY_VALUE="your-actual-api-key"
export DATABASE_URL_VALUE="postgresql://user:pass@host:5432/db"
```

**Option B: Use a `.env` file**

Create a `.env` file (or add to your existing one):

```bash
# .env
API_KEY_VALUE=your-actual-api-key
DATABASE_URL_VALUE=postgresql://user:pass@host:5432/db
```

The `cre` CLI will automatically load variables from `.env` when you run the commands.

> **TIP: Use 1Password CLI**
>
> For enhanced security, use [1Password CLI](/cre/guides/workflow/secrets/managing-secrets-1password) to inject secrets without storing them in plaintext files.

### Step 3: Upload secrets to the Vault DON

Use the `cre secrets create` command to upload your secrets to the Vault:

```bash
cre secrets create production-secrets.yaml --target production-settings
```

**What happens:**

1. The CLI reads your YAML file and environment variables
2. It registers the request onchain (for authorization)
3. It submits the secrets to the Vault DON
4. The secrets are stored securely and associated with your owner address

**Example output:**

```bash
{"level":"info","owner":"<your-owner-address>","digest":"041eb7a8...","time":"2025-10-22T00:14:56+02:00","message":"IsRequestAllowlisted query succeeded"}
{"level":"info","digest":"041eb7a8...","deadline":"2025-10-23T22:14:56Z","time":"2025-10-22T00:14:59+02:00","message":"AllowlistRequest submitted"}
Digest allowlisted; proceeding to gateway POST
Secret created: secret_id=API_KEY, owner=<your-owner-address>, namespace=main
Secret created: secret_id=DATABASE_URL, owner=<your-owner-address>, namespace=main
```

> **NOTE: Default timeout**
>
> Secrets operations have a 48-hour timeout by default. You can customize this with the `--timeout` flag (e.g., `--timeout 1h` or `--timeout 7d`).

### Step 4: Use secrets in your workflow code

Your workflow code uses the same API to access secrets, whether running in local simulation or deployed to a workflow DON. The CRE runtime automatically retrieves secrets from the appropriate source.

> **NOTE: Namespace parameter**
>
> The `namespace` parameter is **optional** and only relevant for deployed workflows using the Vault DON. If omitted, it defaults to `"main"`. For local simulation, the namespace parameter is ignored—secrets are read from your `.env` file regardless.

<CodeHighlightBlockMulti
  languages={{
  ts: {
    code: `import { type Runtime } from "@chainlink/cre-sdk"

const onCronTrigger = (runtime: Runtime<Config>): string => {
// Fetch the secret from the Vault DON (uses default "main" namespace)
const secret = runtime.getSecret({ id: "API_KEY" }).result()
const apiKey = secret.value

runtime.log(\`Using API key: \${apiKey.substring(0, 4)}...\`)

// Use the secret in your workflow logic
// ...

return "Success"
}`,
    title: "Fetching Secrets (TypeScript)",
  },
  go: {
    code: `import (
"github.com/smartcontractkit/cre-sdk-go/cre"
protos "github.com/smartcontractkit/chainlink-protos/cre/go/sdk"
)

func onCronTrigger(config *Config, runtime cre.Runtime, trigger *cron.Payload) (\*MyResult, error) {
logger := runtime.Logger()

  // Fetch the secret from the Vault DON (uses default "main" namespace)
  secretReq := &protos.SecretRequest{Id: "API_KEY"}
  secret, err := runtime.GetSecret(secretReq).Await()
  if err != nil {
  	return nil, err
  }

  apiKey := secret.Value
  logger.Info("Using API key", "prefix", apiKey[:4])

  // Use the secret in your workflow logic
  // ...

  return &MyResult{}, nil

}`,
title: "Fetching Secrets (Go)",
},
}}
/>

**Important:**

- The secret identifier (`"API_KEY"`) must match what you declared in your YAML file
- Secrets are fetched at runtime from the Vault DON
- The namespace parameter is optional—defaults to `"main"` if omitted
- The same code works for both simulation (reads from `.env`) and production (reads from Vault)

### Step 5: Verify secrets are stored

You can list all secrets stored in the Vault for your owner address:

```bash
cre secrets list --target production-settings
```

**Example output:**

```
{"level":"info","owner":"<your-owner-address>","digest":"225d8b6f...","time":"2025-10-22T19:10:12-05:00","message":"IsRequestAllowlisted query succeeded"}
{"level":"info","digest":"225d8b6f...","deadline":"2025-10-25T00:10:12Z","time":"2025-10-22T19:10:16-05:00","message":"AllowlistRequest submitted"}

Digest allowlisted; proceeding to gateway POST: owner=<your-owner-address>, requestID=f9148fcb-3e4e-45bf-bbde-2124ddd577e4, digest=0x225d8b6f...
Secret identifier: secret_id=API_KEY, owner=<your-owner-address>, namespace=main
Secret identifier: secret_id=DATABASE_URL, owner=<your-owner-address>, namespace=main
```

> **NOTE: Security: List only shows IDs**
>
> The `list` command only shows secret identifiers, never the actual secret values. Secret values are only accessible to your deployed workflows at runtime.

## Managing secrets lifecycle

### Updating secrets

To update existing secrets, use the `cre secrets update` command:

```bash
# Update your environment variable with the new value
export API_KEY_VALUE="new-api-key-value"

# Update the secret in the Vault
cre secrets update production-secrets.yaml --target production-settings
```

**Example output:**

```
{"level":"info","owner":"<your-owner-address>","digest":"10854ac2...","time":"2025-10-22T19:12:32-05:00","message":"IsRequestAllowlisted query succeeded"}
{"level":"info","digest":"10854ac2...","deadline":"2025-10-25T00:12:32Z","time":"2025-10-22T19:12:40-05:00","message":"AllowlistRequest submitted"}

Digest allowlisted; proceeding to gateway POST: owner=<your-owner-address>, requestID=7433514f-4008-46dd-822a-633732b64ec9, digest=0x10854ac2...
Secret updated: secret_id=API_KEY, owner=<your-owner-address>, namespace=main
Secret updated: secret_id=DATABASE_URL, owner=<your-owner-address>, namespace=main
```

> **NOTE: Update vs. Create**
>
> - `create` adds new secrets

- `update` modifies existing secrets
- You cannot update a secret that doesn't exist—use `create` first

### Deleting secrets

To remove secrets from the Vault:

**Step 1: Create a deletion YAML file** (`secrets-to-delete.yaml`):

```yaml
secretsNames:
  - API_KEY
  - DATABASE_URL
```

> **NOTE: Delete uses a simpler YAML format**
>
> Unlike `create` and `update` which map secret IDs to environment variables, the `delete` command uses a simple list of secret IDs under `secretsNames`.

**Step 2: Run the delete command:**

```bash
cre secrets delete secrets-to-delete.yaml --target production-settings
```

> **CAUTION: Permanent deletion**
>
> Deleting secrets is permanent and cannot be undone. Double-check your secret identifiers before running this command.

## About namespaces

When you look at CLI outputs, you'll notice secrets are organized by **namespaces**. A namespace is simply a way to group related secrets together.

> **NOTE: Default namespace**
>
> In the current CLI version, all secrets operations (`create`, `update`, `delete`) automatically use the default `"main"` namespace. This is why you see `namespace=main` in the output. Custom namespace support for these commands may be added in future CLI versions.

## Using with multi-sig wallets

All `cre secrets` commands support the `--unsigned` flag for multi-sig wallet operations. This generates raw transaction data instead of sending transactions directly.

For complete multi-sig setup and usage, see [Using Multi-sig Wallets](/cre/guides/operations/using-multisig-wallets).

## Troubleshooting

### "Secret not found" error in deployed workflow

**Problem:** Your workflow throws a "secret not found" error when calling `runtime.GetSecret()`.

**Solution:**

1. Verify the secret exists: `cre secrets list --target production-settings`
2. Check that the secret ID in your code matches exactly
3. Recreate the secret if necessary: `cre secrets create ...`

### "Timeout expired" error

**Problem:** The CLI returns a timeout error when creating/updating secrets.

**Solution:**
The onchain authorization has expired. Re-run the command to create a new authorization.

### Different secrets for simulation vs. production

**Problem:** You want different secret values when simulating vs. running in production.

**Solution:**

- For simulation: Store values in your local `.env` file
- For production: Use `cre secrets create` with different values
- The secret IDs stay the same—only the values differ

## Learn more

- **[Secrets CLI Reference](/cre/reference/cli/secrets)** — Complete CLI command documentation
- **[Using Secrets in Simulation](/cre/guides/workflow/secrets/using-secrets-simulation)** — For local development
- **[Managing Secrets with 1Password](/cre/guides/workflow/secrets/managing-secrets-1password)** — Best practice for secure secret management
- **[Using Multi-sig Wallets](/cre/guides/operations/using-multisig-wallets)** — For multi-sig secret operations