XO Docs

Secrets API

Read and write env vars for the active agent.

The secrets API is a thin layer over the active agent's .env file (typically ~/.openclaw/.env). It powers the Settings → Env Vars UI and onboarding "do you have an ANTHROPIC_API_KEY?" checks.

GET /api/secrets/env returns plaintext values, including secrets. Only the workspace itself should expose this surface; never tunnel it past the trust boundary without an auth layer.

Endpoint summary

VerbPathPurpose
GET/api/secrets/envList all entries with values. Plaintext on the wire.
GET/api/secrets/env/keysNames only (non-empty values). No secret material on the wire.
PUT/api/secrets/envReplace the entire env file with the given entries.

1. GET /api/secrets/env

Response (200 OK)

{
  "entries": [
    { "key": "ANTHROPIC_API_KEY",  "value": "sk-ant-..." },
    { "key": "OPENAI_API_KEY",     "value": "sk-proj-..." },
    { "key": "GITHUB_TOKEN",       "value": "ghp_..." }
  ]
}

The reader (load_env_entries) parses ~/.openclaw/.env:

  • Skips blank lines and lines starting with #.
  • Splits on the first =. Strips both sides.
  • Returns a list of {key, value} pairs in file order.

2. GET /api/secrets/env/keys

Use this on onboarding flows when you need to detect whether a key is set without exposing its value.

Response (200 OK)

{
  "keys": ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GITHUB_TOKEN"]
}

Only keys with non-empty values are listed. Blank-valued keys (KEY=) are filtered out.

3. PUT /api/secrets/env

Overwrite the entire .env file.

Request

{
  "entries": [
    { "key": "ANTHROPIC_API_KEY", "value": "sk-ant-new..." },
    { "key": "OPENAI_API_KEY",    "value": "sk-proj-..." }
  ]
}

Behavior

1. Truncate the .env file.
2. For each entry with non-empty `key`:
     write `{key}={value}\n`
3. Entries with empty `key` are skipped silently.

This is a full overwrite. Comments, blank lines, and any keys not in the request body are lost. There is no merge mode here.

If you want to update a single key without losing comments or unrelated keys, use the onboarding "Save Key" flow inside routers/cowork_agent/config.py, which goes through upsert_env_entry() (line-level edit). That path is not part of /api/secrets/*.

Response (200 OK)

{ "ok": true }

Frontend pattern

A typical settings UI:

// Load
const { entries } = await fetch(`${BASE}/api/secrets/env`).then(r => r.json());
setForm(entries);

// Save (after user edits)
await fetch(`${BASE}/api/secrets/env`, {
  method: "PUT",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ entries: form }),
});

For a "is this connected?" indicator without exposing the value:

const { keys } = await fetch(`${BASE}/api/secrets/env/keys`).then(r => r.json());
const hasAnthropic = keys.includes("ANTHROPIC_API_KEY");

Security notes

  • The .env file lives at ~/.openclaw/.env (or whichever path the active agent's manifest declares).
  • The cowork-api process reads and writes it directly. There is no DB, no encryption-at-rest, no per-user scoping.
  • Workspace = trust boundary. Anyone who can reach the API endpoint can read every secret.

On this page