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
/v1/tenants/me/secretsCreate 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"
}| Code | Meaning | When |
|---|---|---|
| 201 | Created | bundle stored |
| 400 | Bad request | invalid name regex, reserved key name, value > 64KB, or bundle > 50 keys |
| 409 | Conflict | bundle with this name already exists |
| 429 | Too many requests | tenant quota exceeded (50 bundles) |
List Secret Bundles
/v1/tenants/me/secretsList 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
/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"
}| Code | Meaning | When |
|---|---|---|
| 200 | OK | bundle found |
| 404 | Not found | no such bundle |
Rotate Secret Bundle (Hot-Replace)
/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"
}| Code | Meaning | When |
|---|---|---|
| 200 | OK | bundle rotated + broadcast sent to running sessions |
| 400 | Bad request | invalid key name, reserved key, value too large |
| 404 | Not found | no 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
/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" }| Code | Meaning | When |
|---|---|---|
| 200 | OK | bundle deleted |
| 404 | Not found | no such bundle |
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