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

# Secrets Module

> Securely store and retrieve sensitive values at runtime

The **secrets** module lets automations securely store and retrieve sensitive values (API keys, tokens, credentials) at runtime. Values are encrypted at rest and never exposed as plaintext in automation outputs or logs.

This page documents two things:

* The **runtime API** (`secrets.set`, `secrets.get`, `secrets.delete`) for automations.
* The **Settings → Secrets** form in the Builder UI, which is where workspace-level secrets are typically configured before any automation runs.

## Settings → Secrets form

Open **Settings → Secrets** in the Builder. The form has two sections.

### Schema-defined secrets

The top section is generated from the workspace's secret **schema**, declared in `index.yml`:

```yaml theme={null}
secrets:
  schema:
    openaiApiKey:
      type: string
      description: API key for the OpenAI integration
    notionToken:
      type: string
      description: Notion integration token
```

Every property of the schema is rendered as an input field. These are the "main" secrets of the workspace: they have known names, automations reference them as `{{secret.<name>}}`, and someone (usually the workspace author) committed the schema to source control.

Typing a value and clicking **Save Secrets** posts the new values to `POST /workspaces/{id}/secrets`. Values are stored encrypted; the form never reads back plaintext (an empty field means "leave the existing value untouched", not "clear the value").

### Additional Secrets form

The "Additional Secrets" section underneath is for secrets that **are not declared in the schema**. The label "Additional" is intentional — these are extras beyond the schema-defined set.

| Field            | Notes                                                                                          |
| ---------------- | ---------------------------------------------------------------------------------------------- |
| **New key**      | The variable name. Will be readable from automations as `{{secret.<key>}}`.                    |
| **New value**    | Hidden by default (password input). The eye icon toggles visibility.                           |
| **+** button     | Adds the pair to the in-memory list. The secret is not persisted until you press Save Secrets. |
| Delete (per row) | Removes the row from the in-memory list.                                                       |
| **Save Secrets** | Persists the schema values *and* the additional secrets in one call.                           |

Use the Additional form when:

* You are prototyping and have not yet committed a schema.
* You need an environment-specific override (e.g. a per-deploy webhook URL) that should not appear in source.
* You want a one-off value usable from a single automation without touching `index.yml`.

Once a name stabilizes, promote it to the schema so the next workspace clone has the documentation built in.

<Warning>
  The "Additional Secrets" form persists plaintext values to the workspace's encrypted secret store. It is **not** a temporary scratchpad — entries survive reloads and pulls (unless overwritten by a pulled `index.yml`).
</Warning>

## Runtime API

For automations that need to **create** or **rotate** secrets at runtime (OAuth tokens, refresh tokens, per-user credentials), the `secrets` module exposes `set`, `get`, and `delete`.

### How it works

1. `secrets.set` and `secrets.get` return an opaque **secret reference** (a `$secret:...` string), not the actual value.
2. When this reference is used inside a `fetch` instruction (in the URL, headers, body, query, or auth fields), the platform automatically resolves it to the real value just before the HTTP call.
3. The resolved value is redacted from all logs and events.

Secret references are short-lived (a few minutes). Always call `secrets.get` right before using the reference — do not store it for later reuse.

### Scopes

| Scope       | Description                                                                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `workspace` | Shared across all users in the workspace. Any workspace editor can read/update it.                                                                     |
| `user`      | Private to the current user. Only accessible by automations running in that user's context. Not available in cron/schedule triggers (no user context). |

### Functions

### set — Store or update a secret

```yaml theme={null}
- run:
    module: secrets
    function: set
    parameters:
      name: myApiKey
      value: "sk-live-abc123"
      scope: workspace
    output: secretRef
```

| Parameter | Type                  | Required | Description                                                                             |
| --------- | --------------------- | -------- | --------------------------------------------------------------------------------------- |
| `name`    | string                | yes      | Unique name for the secret (within its scope)                                           |
| `value`   | any                   | yes      | The value to encrypt and store                                                          |
| `scope`   | `user` \| `workspace` | yes      | Storage scope                                                                           |
| `ttl`     | number                | no       | Time-to-live in seconds. The secret expires and becomes unreadable after this duration. |

Returns a `$secret:...` reference string. If a secret with the same name and scope already exists, it is updated.

### get — Retrieve a secret reference

```yaml theme={null}
- run:
    module: secrets
    function: get
    parameters:
      name: myApiKey
      scope: workspace
    output: secretRef
```

| Parameter | Type                  | Required | Description                    |
| --------- | --------------------- | -------- | ------------------------------ |
| `name`    | string                | yes      | Name of the secret to retrieve |
| `scope`   | `user` \| `workspace` | yes      | Storage scope                  |

Returns a `$secret:...` reference string, or `{ error: "not_found" }` if the secret does not exist (or has expired).

### delete — Remove a secret

```yaml theme={null}
- run:
    module: secrets
    function: delete
    parameters:
      name: myApiKey
      scope: workspace
    output: result
```

| Parameter | Type                  | Required | Description                  |
| --------- | --------------------- | -------- | ---------------------------- |
| `name`    | string                | yes      | Name of the secret to delete |
| `scope`   | `user` \| `workspace` | yes      | Storage scope                |

Returns `{ deleted: true }` on success, or `{ error: "not_found" }`.

### Using secret references in fetch

Pass the reference returned by `set` or `get` wherever a sensitive value is needed in a `fetch` instruction. The platform resolves it transparently:

```yaml theme={null}
do:
  # 1. Retrieve the secret reference
  - run:
      module: secrets
      function: get
      parameters:
        name: myApiKey
        scope: workspace
      output: secretRef

  # 2. Use it in a fetch — the platform injects the real value at execution time
  - fetch:
      url: https://api.example.com/data
      method: GET
      headers:
        Authorization: "Bearer {{secretRef}}"
      output: apiResponse
```

Resolution is supported in these `fetch` fields: `url`, `headers`, `body`, `query`, and `auth` (including `auth.awsv4`).

<Warning>
  Secret references expire after a few minutes. Always retrieve the reference (`secrets.get`) in the same automation run where you use it.
</Warning>

### Complete example — storing and using an OAuth token

```yaml theme={null}
slug: call-with-oauth
name: Call API with OAuth token
do:
  # Authenticate and get a token
  - fetch:
      url: https://auth.example.com/oauth/token
      method: POST
      body:
        grant_type: client_credentials
        client_id: "{{config.clientId}}"
        client_secret: "{{secret.clientSecret}}"
      output: authResponse

  # Store the token as a workspace secret with a 1-hour TTL
  - run:
      module: secrets
      function: set
      parameters:
        name: oauthToken
        value: "{{authResponse.access_token}}"
        scope: workspace
        ttl: 3600
      output: tokenRef

  # Use the token in subsequent API calls
  - fetch:
      url: https://api.example.com/resources
      method: GET
      headers:
        Authorization: "Bearer {{tokenRef}}"
      output: resources
```

### Error handling

All three functions return an error object instead of throwing when the error is actionable:

| Error                        | Returned by            | Meaning                                                                 |
| ---------------------------- | ---------------------- | ----------------------------------------------------------------------- |
| `{ error: "not_found" }`     | `get`, `delete`        | No secret with that name/scope exists (or it has expired)               |
| `{ error: "user_required" }` | `set`, `get`, `delete` | `scope: user` was used but there is no user context (e.g. cron trigger) |

Use `onError` or a `conditions` block to handle these:

```yaml theme={null}
- run:
    module: secrets
    function: get
    parameters:
      name: myApiKey
      scope: workspace
    onError: continue
    output: result
- conditions:
    '{{result.error}}':
      - emit:
          event: secret-missing
          payload:
            message: "Secret myApiKey not found"
```
