GET
/
api
/
v1
/
workspaces
/
{slug}
/
api-keys
curl https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"
curl -X POST https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Slideshow CI",
    "permissions": ["log_usage", "read_summaries"],
    "monthly_limit": 75,
    "rate_limit_rpm": 120
  }'
curl -X PATCH https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys/{id} \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "monthly_limit": 100 }'
curl -X POST https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys/{id}/rotate \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"
curl -X DELETE https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys/{id} \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"
All management routes require a Supabase user JWT + workspace membership. Guide: API keys & conditions. Ingest with the secret via Authorization: Bearer cmcp_live_... on REST or https://mcp.costmcp.com.

List keys

GET /api/v1/workspaces/{slug}/api-keys
Returns { keys: [...] } including conditions and spent_usd (month-to-date when monthly_limit is set). Secrets are never returned. Fields: id, name, key_prefix, permissions, environment, status, project_id, monthly_limit, expires_at, rate_limit_rpm, allowed_cidrs, conditions, last_used_at, created_at, spent_usd.

Create key

POST /api/v1/workspaces/{slug}/api-keys
name
string
required
Display name
permissions
string[]
Defaults to log_usage, add_expenses, read_summaries, estimate_costs
project_id
string
Restrict the key to a single project UUID (null = all projects)
project_slugs
string[]
Optional allow-list stored in conditions.project_slugs (ignored when project_id is set)
monthly_limit
number
Max USD attributed to this key in the current UTC month
expires_at
string
ISO datetime; requests after this return 401 key_expired
rate_limit_rpm
integer
Max requests per rolling minute (429 rate_limited)
allowed_cidrs
string[]
Empty = allow all. Otherwise client IP must match (403 ip_not_allowed)
environment
string
live (default) or test
conditions
object
Extensible JSON: project_slugs, deny_project_slugs, features, sources, notes
201 response includes secret (cmcp_live_...) — shown once. Valid permissions: log_usage, add_expenses, read_summaries, estimate_costs, manage_subscriptions, delete_records.

Update conditions

PATCH /api/v1/workspaces/{slug}/api-keys/{id}
Same body fields as create (all optional). Does not rotate the secret. Creates an audit event (updated).

Rotate secret

POST /api/v1/workspaces/{slug}/api-keys/{id}/rotate
Issues a new secret (shown once) and invalidates the previous hash. Conditions are preserved. Audit action: rotated.

Revoke key

DELETE /api/v1/workspaces/{slug}/api-keys/{id}
Soft-revokes (status = "revoked"). Returns { ok: true }. Audit action: revoked.

Enforcement on ingest

ConditionHTTPError code
Expired401key_expired
IP not allowed403ip_not_allowed
Rate exceeded429rate_limited
Missing permission403missing_permission
Wrong project403project_not_allowed
Monthly cap403monthly_limit_exceeded
Wrong source / feature403source_not_allowed / feature_not_allowed
Ledger rows stamped with api_key_id power the monthly spend meter.
curl https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"
curl -X POST https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Slideshow CI",
    "permissions": ["log_usage", "read_summaries"],
    "monthly_limit": 75,
    "rate_limit_rpm": 120
  }'
curl -X PATCH https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys/{id} \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "monthly_limit": 100 }'
curl -X POST https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys/{id}/rotate \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"
curl -X DELETE https://api.costmcp.com/api/v1/workspaces/acme-ai/api-keys/{id} \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"