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

# Secrets

> Store credentials for an App outside its source.

Secrets store sensitive values—such as third-party API keys, webhook signing keys, and database credentials—outside your App's source files and Git history. Judt encrypts Secrets at rest and never returns their values in API responses, MCP tool outputs, or agent transcripts.

For non-secret configuration values that belong in version control, use the `vars` object in `wrangler.json` instead. See [Vars and Secrets](https://docs.judt.ai/build/vars-and-secrets.md).

## Naming and size rules

Every Secret belongs to one App and must follow these rules:

* **Name**: `1` to `128` characters matching `^[A-Z_][A-Z0-9_]{0,127}$`. Use uppercase ASCII letters (`A-Z`), digits (`0-9`), and underscores (`_`). A name cannot start with a digit.
* **Value**: Non-empty UTF-8 string up to `64 KiB` (`65,536` bytes).

If a Secret has the same name as a key in the `vars` object of `wrangler.json`, the Secret's value overrides the `vars` key at runtime.

## How Secrets reach your App

In your Worker code, read a Secret from the `env` argument passed to your handler (`env.SECRET_NAME`):

```javascript index.js theme={null}
export default {
  async fetch(request, env) {
    const response = await fetch("https://api.stripe.com/v1/charges", {
      headers: {
        Authorization: `Bearer ${env.STRIPE_SECRET_KEY}`,
      },
    });
    return new Response(await response.text(), { status: response.status });
  },
};
```

Secrets are delivered differently to the development sandbox and to production:

| Environment | How Secrets are delivered | When changes take effect |
| - | - | - |
| **Sandbox dev server (`celld dev`)** | Written to `/app/.dev.vars`, which the dev server reads. | **At once** if the sandbox is running. Otherwise, at the next sandbox start. |
| **Sandbox commands (`POST /api/apps/{id}/commands`)** | Not delivered. A command does not get the App's Secrets, and its output shows any Secret value as `[secret:NAME]`. | Does not apply. |
| **Production deployment** | Injected into the deployed runtime configuration during `POST /api/apps/{id}/deployments`. Secrets are **not** available during `npm run build`. | On the **next deployment**. Changing a Secret does not alter an already-published deployment until you deploy again. |

## Manage Secrets with the API

Only organization `owner` and `admin` roles can create, update, or delete Secrets. Organization `member` roles can list Secret names and metadata.

### Create or update a Secret

Call `PUT /api/apps/{id}/secrets/{name}` to create a Secret or replace its value:

```bash theme={null}
curl -X PUT https://api.judt.ai/api/apps/$APP_ID/secrets/STRIPE_SECRET_KEY \
  -H "Authorization: Bearer $JUDT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "sk_live_51H..."}'
```

The API returns the Secret's metadata without its value:

```json theme={null}
{
  "id": "sec_5mK9pQ2xRtV7nW3sLc8ZbY4j",
  "app_id": "app_8fK2mQ7xRtV3nW9pLc4ZbY1s",
  "name": "STRIPE_SECRET_KEY",
  "created_at": "2026-10-15T20:25:00.000Z",
  "updated_at": "2026-10-15T20:25:00.000Z"
}
```

### List or inspect Secrets

Call `GET /api/apps/{id}/secrets` to list all Secrets on an App, or `GET /api/apps/{id}/secrets/{name}` (using the Secret name, such as `STRIPE_SECRET_KEY`) to check whether a specific Secret exists.

### Delete a Secret

Call `DELETE /api/apps/{id}/secrets/{name}`:

```bash theme={null}
curl -X DELETE https://api.judt.ai/api/apps/$APP_ID/secrets/STRIPE_SECRET_KEY \
  -H "Authorization: Bearer $JUDT_API_KEY"
```

After you delete a Secret, the dev server of a running sandbox stops getting it at once. To remove it from production, create a new deployment (`POST /api/apps/{id}/deployments`).

## Provide Secrets to a waiting agent turn

When the [in-product agent](https://docs.judt.ai/platform-integrations.md#build-a-workflow-around-the-judt-agent) needs one or more Secrets to finish a task, its turn pauses with `status: "waiting"` and an `input_request` of `kind: "secrets"`:

```json theme={null}
{
  "id": "INPUT_REQUEST_ID",
  "kind": "secrets",
  "secrets": [
    {
      "name": "STRIPE_SECRET_KEY",
      "provided": false
    }
  ],
  "reason": "I need the Stripe API key to test the checkout endpoint.",
  "expires_at": "2026-10-03T14:01:30.000Z"
}
```

If no one provides or declines the Secrets before `expires_at`, the turn fails with `input_expired`.

As soon as you set every requested Secret (in the dashboard chat card or via `PUT /api/apps/{id}/secrets/{name}`), Judt automatically marks the request resolved and resumes the agent turn. If you do not want to provide the Secret, call `POST /api/apps/{id}/sessions/{session_id}/turns/{turn_id}/decline` so the agent continues without it.

> [!WARNING]
>
> Do not put real Secret values in an external LLM chat prompt. The model provider can see them in its conversation context. Instead, set the Secret in the Judt dashboard or from your terminal with `PUT /api/apps/{id}/secrets/{name}`. No MCP tool takes a Secret value: `request_secrets` gives the user a link to the dashboard, and `generate_secret` makes a random value that never passes through the conversation.

## Next steps

* Learn how `env` bindings work in Worker code in [Vars and Secrets](https://docs.judt.ai/build/vars-and-secrets.md).
* Learn how the in-product agent requests Secrets in [Build a workflow around the Judt agent](https://docs.judt.ai/platform-integrations.md#build-a-workflow-around-the-judt-agent).


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