Keys API
title: Keys API audience: Developer difficulty: Intermediate estimated_read_time: 7 min prerequisites:
- Authentication setup
- Understanding of keys vs bundles related_pages:
- ../concepts/bundles.md
- ../api-reference/bundles.md
Manage keys - reusable environment variables in your library.
Overview
Keys are individual environment variables stored in a centralized library. They can be:
- Attached to multiple bundles
- Updated once (propagates to all bundles)
- Marked as secret (values masked in UI) or non-secret
- Searched and filtered
Base URL: https://env.cat/api/v1/keys
Authentication: All endpoints require authentication (Clerk session → BFF signature)
Endpoints
List Keys
List all keys in current tenant.
Endpoint:
GET /api/v1/keys
Authorization: Organization members (role-based access)
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | No | Search by key name (case-insensitive) |
Response (200 OK):
[
{
"id": "key-uuid-1",
"name": "DATABASE_URL",
"value": "*****",
"is_secret": true,
"description": "PostgreSQL connection string",
"created_at": "2025-10-22T10:00:00Z",
"updated_at": "2025-10-22T10:00:00Z",
"used_in_bundles": 3
},
{
"id": "key-uuid-2",
"name": "PORT",
"value": "3000",
"is_secret": false,
"description": "Application port",
"created_at": "2025-10-22T10:05:00Z",
"updated_at": "2025-10-22T10:05:00Z",
"used_in_bundles": 5
}
]
Notes:
- Secret keys show
value: "*****"(masked) - Non-secret keys show actual decrypted value
used_in_bundlesshows how many bundles use this key
Example:
# List all keys
curl https://env.cat/api/v1/keys \
-H "Cookie: __session=your_clerk_session"
# Search keys
curl "https://env.cat/api/v1/keys?query=DATABASE" \
-H "Cookie: __session=your_clerk_session"
Create Key
Create a new key in the library.
Endpoint:
POST /api/v1/keys
Authorization: Organization members (all members can create keys)
Request Body:
{
"name": "DATABASE_URL",
"value": "postgres://user:pass@localhost:5432/db",
"is_secret": true,
"description": "PostgreSQL connection string"
}
Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Key name (unique per tenant, UPPERCASE_SNAKE_CASE recommended) |
value | string | Yes | - | Key value (encrypted at-rest) |
is_secret | boolean | No | true | If true, value masked in UI |
description | string | No | "" | Human-readable description |
Response (201 Created):
{
"id": "key-uuid",
"name": "DATABASE_URL",
"value": "*****",
"is_secret": true,
"description": "PostgreSQL connection string",
"created_at": "2025-10-22T10:00:00Z",
"updated_at": "2025-10-22T10:00:00Z",
"used_in_bundles": 0
}
Errors:
| Code | Description |
|---|---|
| 400 | Name or value required, or invalid format |
| 403 | Forbidden (not owner) |
| 409 | Key with this name already exists |
Example:
curl -X POST https://env.cat/api/v1/keys \
-H "Content-Type: application/json" \
-H "Cookie: __session=your_clerk_session" \
-d '{
"name": "DATABASE_URL",
"value": "postgres://user:pass@localhost:5432/db",
"is_secret": true,
"description": "PostgreSQL connection string"
}'
Get Key
Get a single key by ID.
Endpoint:
GET /api/v1/keys/:key_id
Authorization: Organization members (role-based access)
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
reveal | string | No | 1 to show decrypted value (secrets always masked unless dev mode) |
Response (200 OK):
{
"id": "key-uuid",
"name": "DATABASE_URL",
"value": "*****",
"is_secret": true,
"description": "PostgreSQL connection string",
"created_at": "2025-10-22T10:00:00Z",
"updated_at": "2025-10-22T10:00:00Z",
"used_in_bundles": 3,
"bundles": [
{
"id": "bundle-uuid-1",
"name": "dev/api"
},
{
"id": "bundle-uuid-2",
"name": "staging/api"
},
{
"id": "bundle-uuid-3",
"name": "prod/api"
}
]
}
Response (200 OK) - With reveal=1 (dev mode only):
{
"id": "key-uuid",
"name": "DATABASE_URL",
"value": "postgres://user:pass@localhost:5432/db",
"is_secret": true,
"description": "PostgreSQL connection string",
"created_at": "2025-10-22T10:00:00Z",
"updated_at": "2025-10-22T10:00:00Z",
"used_in_bundles": 3,
"bundles": [...]
}
Errors:
| Code | Description |
|---|---|
| 403 | Forbidden (not member) or reveal not allowed |
| 404 | Key not found |
Example:
# Get key (masked)
curl https://env.cat/api/v1/keys/key-uuid \
-H "Cookie: __session=your_clerk_session"
# Get key (revealed - dev mode only)
curl "https://env.cat/api/v1/keys/key-uuid?reveal=1" \
-H "Cookie: __session=your_clerk_session"
Update Key
Update a key's value or description. Changes propagate to all bundles.
Endpoint:
PUT /api/v1/keys/:key_id
Authorization: Organization admins only
Request Body:
{
"value": "postgres://user:newpass@localhost:5432/db",
"description": "Updated PostgreSQL connection string"
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
value | string | No | New value (if provided, encrypted at-rest) |
description | string | No | New description |
is_secret | boolean | No | Update secret flag |
Response (200 OK):
{
"id": "key-uuid",
"name": "DATABASE_URL",
"value": "*****",
"is_secret": true,
"description": "Updated PostgreSQL connection string",
"created_at": "2025-10-22T10:00:00Z",
"updated_at": "2025-10-22T11:00:00Z",
"used_in_bundles": 3
}
Errors:
| Code | Description |
|---|---|
| 403 | Forbidden (not owner) |
| 404 | Key not found |
Example:
curl -X PUT https://env.cat/api/v1/keys/key-uuid \
-H "Content-Type: application/json" \
-H "Cookie: __session=your_clerk_session" \
-d '{
"value": "postgres://user:newpass@localhost:5432/db",
"description": "Updated connection string"
}'
Important: Updating a key's value immediately affects all bundles using this key.
Delete Key
Delete a key from the library. Removes from all bundles.
Endpoint:
DELETE /api/v1/keys/:key_id
Authorization: Organization admins only
Response (200 OK):
{
"ok": true
}
Errors:
| Code | Description |
|---|---|
| 403 | Forbidden (not owner) |
| 404 | Key not found |
Example:
curl -X DELETE https://env.cat/api/v1/keys/key-uuid \
-H "Cookie: __session=your_clerk_session"
Warning: Deleting a key removes it from all bundles using it. This action cannot be undone.
Key Propagation
Keys are reusable and propagate changes automatically.
How It Works
- Create key:
DATABASE_URL = postgres://localhost:5432/db - Attach to bundles:
dev/api,staging/api,prod/api - Update key value:
DATABASE_URL = postgres://newhost:5432/db - Automatic propagation: All 3 bundles now have new value
Benefits
- Single source of truth: Update once, affects all bundles
- Consistency: No risk of mismatched values across environments
- Efficiency: No need to update each bundle individually
Use Cases
Rotating secrets:
# 1. Update key value
curl -X PUT https://env.cat/api/v1/keys/stripe-key-uuid \
-d '{"value":"sk_live_NEW_KEY"}'
# 2. All bundles using STRIPE_SECRET_KEY now have new value
# No need to update each bundle manually
Shared configuration:
# Create key used across all environments
curl -X POST https://env.cat/api/v1/keys \
-d '{
"name": "LOG_LEVEL",
"value": "info",
"is_secret": false
}'
# Attach to dev, staging, and prod bundles
# Update once to change all environments
Best Practices
Naming Conventions
Use SCREAMING_SNAKE_CASE:
✅ DATABASE_URL
✅ STRIPE_SECRET_KEY
✅ MAX_CONNECTIONS
❌ databaseUrl
❌ stripe-secret-key
❌ maxConnections
Be descriptive:
✅ POSTGRES_PRIMARY_DATABASE_URL
✅ STRIPE_LIVE_SECRET_KEY
✅ REDIS_SESSION_CACHE_URL
❌ DB_URL
❌ KEY
❌ CACHE
Organizing Keys
Group related keys:
POSTGRES_HOST
POSTGRES_PORT
POSTGRES_DATABASE
POSTGRES_USER
POSTGRES_PASSWORD
Use descriptions:
{
"name": "STRIPE_SECRET_KEY",
"description": "Production Stripe secret key (live mode). Rotate every 90 days."
}
Security
Mark secrets correctly:
- API keys, passwords, tokens:
is_secret: true - Ports, URLs, flags:
is_secret: false
Rotate regularly:
- Update key values periodically
- Especially after team member departures
- Use description to track rotation schedule
Audit usage:
- Check
used_in_bundlesbefore deleting - Review which bundles use sensitive keys
- Remove unused keys
See Also
- Bundles API - Attach keys to bundles
- Authentication - Auth flow
- Errors - Error codes
- User Guide: Keys - Keys conceptual guide