Secrets API

Manage secret bundles for BYO LLM keys. Keys are stored in AWS Secrets Manager, injected at session start, and support hot-replace without restart.

Key Concepts

  • LLM_API_KEY(required) universal key name for LLM credentials, regardless of vendor (Anthropic, OpenAI, DeepSeek, etc.)
  • LLM_BASE_URL(optional) custom API endpoint for non-Anthropic providers
  • LLM_MODEL(optional) model identifier for non-standard providers
  • Bundle — a named collection of key-value pairs stored under your tenant namespace
  • Hot-replace — rotate keys via PUT and running sessions receive new values via WebSocket without restart
  • Caps — max 50 bundles/tenant, 50 keys/bundle, 64KB/value, 10 bundle refs/session

BYO LLM Providers

You can configure LLM settings in secrets, in the session request, or both. If both are set, session request takes precedence.

Option 1: Full config in secrets (recommended for hot-replace)

{
  "name": "deepseek-keys",
  "values": {
    "LLM_API_KEY": "sk-...",                       // required
    "LLM_BASE_URL": "https://api.coding67.com/v1", // optional
    "LLM_MODEL": "deepseek-v4-pro"                 // optional
  }
}

Option 2: Key in secrets, config in session request

POST /v1/sessions
{
  "llm": {
    "vendor": "openai",                           // openai-compatible endpoint
    "model": "deepseek-v4-pro",
    "base_url": "https://api.coding67.com/v1"
  },
  "secrets": ["my-llm-keys"]                      // bundle with LLM_API_KEY only
}

Any OpenAI-compatible endpoint works. Use Option 1 if you want to hot-replace the entire LLM config via PUT without restarting sessions.

Create Secret Bundle

POST/v1/tenants/me/secrets

Create a new secret bundle. Values are stored encrypted in AWS Secrets Manager.

Request (Anthropic — default)

{
  "name": "my-llm-keys",              // regex: ^[a-z0-9][a-z0-9-]{0,62}$
  "values": {
    "LLM_API_KEY": "sk-ant-..."       // only LLM_API_KEY needed for Anthropic
  }
}

Request (non-standard provider)

{
  "name": "deepseek-keys",
  "values": {
    "LLM_API_KEY": "sk-...",
    "LLM_BASE_URL": "https://api.coding67.com/v1",
    "LLM_MODEL": "deepseek-v4-pro"
  }
}

Response (201)

{
  "name": "my-llm-keys",
  "key_names": ["LLM_API_KEY"],       // values are NEVER returned
  "created_at": "2026-05-01T12:00:00Z"
}
CodeMeaningWhen
201Createdbundle stored
400Bad requestinvalid name regex, reserved key name, value > 64KB, or bundle > 50 keys
409Conflictbundle with this name already exists
429Too many requeststenant quota exceeded (50 bundles)
Reserved keys: AGENT_TYPE, LLM_KEY, TASK, SESSION_ID, AGENT_CMD, AGENT_SESSION_MODE, E2A_PERSISTENCE_URL, E2A_PERSISTENCE_AUTH_HEADER

List Secret Bundles

GET/v1/tenants/me/secrets

List all secret bundles. Values are NEVER returned.

Response (200)

{
  "bundles": [
    {
      "name": "my-llm-keys",
      "key_names": ["LLM_API_KEY"],
      "created_at": "2026-05-01T12:00:00Z",
      "last_rotated_at": "2026-05-01T14:00:00Z"
    }
  ]
}

Get Secret Bundle

GET/v1/tenants/me/secrets/{name}

Get metadata for a specific bundle. Values are NEVER returned.

Response (200)

{
  "name": "my-llm-keys",
  "key_names": ["LLM_API_KEY"],
  "created_at": "2026-05-01T12:00:00Z",
  "last_rotated_at": "2026-05-01T14:00:00Z"
}
CodeMeaningWhen
200OKbundle found
404Not foundno such bundle

Rotate Secret Bundle (Hot-Replace)

PUT/v1/tenants/me/secrets/{name}

Replace all values in an existing bundle. Running sessions receive the new values in-place via WebSocket config_update frame — no restart required.

Request

{
  "values": {
    "LLM_API_KEY": "sk-ant-new-key..."     // new value
  }
}

Response (200)

{
  "name": "my-llm-keys",
  "key_names": ["LLM_API_KEY"],
  "last_rotated_at": "2026-05-01T15:00:00Z"
}
CodeMeaningWhen
200OKbundle rotated + broadcast sent to running sessions
400Bad requestinvalid key name, reserved key, value too large
404Not foundno such bundle

WebSocket frame received by running sessions

{ "type": "system", "event": "config_update", "keys": ["LLM_API_KEY"] }

The agent runtime swaps in the new values without interrupting the current task.

Delete Secret Bundle

DELETE/v1/tenants/me/secrets/{name}

Delete a secret bundle. Running sessions continue with their current values. Future resume calls return 502.

Response (200)

{ "status": "deleted" }
CodeMeaningWhen
200OKbundle deleted
404Not foundno such bundle
Warning: Sessions that referenced this bundle will fail to resume with 502 secret_bundle_not_found_at_resume.

Using Secrets with Sessions

Reference bundles by name in POST /v1/sessions:

POST /v1/sessions
{
  "agent": { "kind": "deictic" },
  "template": "standard",
  "llm": { "vendor": "anthropic", "model": "claude-sonnet-4-5" },
  "secrets": ["my-llm-keys"],              // bundle names
  "user_id": "usr_abc"
}
  • Up to 10 bundles per session
  • Keys are merged; collision across bundles returns 400
  • Resume recovers secrets references automatically