Api Key Management Blueprint
Create, rotate, revoke, and scope API keys for programmatic access
| Feature | api-key-management |
| Category | Auth |
| Version | 1.0.0 |
| Tags | authentication, api-key, security, programmatic-access, developer |
| YAML Source | View on GitHub |
| JSON API | api-key-management.json |
Fields
| Name | Type | Required | Label | Description |
|---|---|---|---|---|
key_id | text | Yes | Key ID | Validations: required |
key_hash | hidden | Yes | Key Hash | |
key_prefix | text | Yes | Key Prefix | Validations: maxLength |
name | text | Yes | Key Name | Validations: required, maxLength, minLength |
scopes | multiselect | Yes | Permissions | Validations: required |
expires_at | datetime | No | Expiration Date | |
last_used_at | datetime | No | Last Used | |
last_used_ip | text | No | Last Used IP | |
created_by | text | Yes | Created By | Validations: required |
created_at | datetime | Yes | Created At | |
revoked_at | datetime | No | Revoked At | |
environment | select | Yes | Environment | Validations: required, oneOf |
Rules
- security:
- key_generation:
- algorithm: cryptographic_random
- length_bytes: 32
- format: sk_{environment}_{random}
- prefix_live: sk_live_
- prefix_test: sk_test_
- show_once: true
- storage:
- hash_algorithm: sha256
- never_store_plaintext: true
- rate_limit_per_key:
- default_window_seconds: 60
- default_max_requests: 100
- burst_allowance: 20
- max_keys_per_user: 25
- rate_limit:
- window_seconds: 60
- max_requests: 10
- scope: per_user
- key_generation:
- rotation:
- grace_period_hours: 24
- notify_on_rotation: true
- expiration:
- warn_before_days: 7
- auto_revoke_on_expiry: true
Outcomes
Rate_limited (Priority: 1) — Error: API_KEY_RATE_LIMITED
Given:
management_request_count(computed) gt10
Result: show “Too many requests. Please wait a moment.”
Max_keys_exceeded (Priority: 2) — Error: API_KEY_LIMIT_EXCEEDED
Given:
active_key_count(db) gte25
Result: show “Maximum number of API keys reached. Please revoke unused keys.”
Create_key (Priority: 5) — Error: API_KEY_PER_KEY_RATE_LIMITED | Transaction: atomic
Given:
name(input) existsscopes(input) existsactive_key_count(db) lt25
Then:
- create_record target:
api_key— Generate cryptographic random key, store hash only - emit_event event:
api_key.created
Result: show full key ONCE — warn user to copy it now as it cannot be retrieved again
Rotate_key (Priority: 6) | Transaction: atomic
Given:
target_key(db) existstarget_key_revoked_at(db) not_existstarget_key_created_by(db) eqcurrent_user_id
Then:
- create_record target:
new_api_key— Generate new key with same name, scopes, and environment - set_field target:
old_key_grace_expiryvalue:now + 24h— Old key remains valid for 24-hour grace period - emit_event event:
api_key.rotated
Result: show new key ONCE — old key valid for 24 more hours
Revoke_key (Priority: 7) — Error: API_KEY_REVOKED | Transaction: atomic
Given:
target_key(db) existstarget_key_revoked_at(db) not_existstarget_key_created_by(db) eqcurrent_user_id
Then:
- set_field target:
revoked_atvalue:now - invalidate target:
api_key— Immediately invalidate the key hash - emit_event event:
api_key.revoked
Result: key revoked — all requests using this key will be rejected immediately
Authenticate_with_key (Priority: 8)
Given:
api_key(request) existsapi_key_hash(computed) eqstored_key_hashkey_revoked_at(db) not_exists- ALL: ANY:
key_expires_at(db) not_exists ORkey_expires_at(db) gtnow
Then:
- set_field target:
last_used_atvalue:now - set_field target:
last_used_ipvalue:request_ip - emit_event event:
api_key.used
Result: request authenticated — enforce scopes on subsequent authorization checks
Key_expired (Priority: 9) — Error: API_KEY_EXPIRED
Given:
api_key_hash(computed) eqstored_key_hashkey_expires_at(db) ltenow
Then:
- set_field target:
revoked_atvalue:now— Auto-revoke expired key - emit_event event:
api_key.expired
Result: show “API key has expired. Please generate a new key.”
Invalid_key (Priority: 10) — Error: API_KEY_INVALID
Given:
api_key(request) existsapi_key_hash(computed) neqstored_key_hash
Then:
- emit_event event:
api_key.invalid_attempt
Result: return 401 Unauthorized (generic message — do not reveal whether key exists)
Errors
| Code | Status | Message | Retry |
|---|---|---|---|
API_KEY_RATE_LIMITED | 429 | Too many requests. Please wait a moment. | Yes |
API_KEY_LIMIT_EXCEEDED | 409 | Maximum number of API keys reached. Please revoke unused keys. | No |
API_KEY_INVALID | 401 | Invalid API key | No |
API_KEY_EXPIRED | 401 | API key has expired | No |
API_KEY_REVOKED | 401 | API key has been revoked | No |
API_KEY_INSUFFICIENT_SCOPE | 403 | API key does not have the required permissions | No |
API_KEY_NOT_FOUND | 404 | API key not found | No |
API_KEY_PER_KEY_RATE_LIMITED | 429 | Rate limit exceeded for this API key | Yes |
Events
| Event | Description | Payload |
|---|---|---|
api_key.created | New API key generated | user_id, key_id, key_prefix, name, scopes, environment, timestamp |
api_key.rotated | API key rotated — new key issued, old key in grace period | user_id, old_key_id, new_key_id, key_prefix, timestamp |
api_key.revoked | API key permanently revoked | user_id, key_id, key_prefix, name, timestamp |
api_key.used | API key used to authenticate a request | key_id, key_prefix, timestamp, ip_address, endpoint |
api_key.expired | API key auto-revoked due to expiration | key_id, key_prefix, timestamp |
api_key.invalid_attempt | Request made with an invalid API key | key_prefix, timestamp, ip_address |
Related Blueprints
| Feature | Relationship | Reason |
|---|---|---|
| login | required | User must be authenticated to manage API keys |
| session-management | recommended | API key management requires an active session |
| multi-factor-auth | recommended | MFA should be required before creating live API keys |
AGI Readiness
Goals
Reliable Api Key Management
Create, rotate, revoke, and scope API keys for programmatic access
Success Metrics:
| Metric | Target | Measurement |
|---|---|---|
| unauthorized_access_rate | 0% | Failed authorization attempts that succeed |
| response_time_p95 | < 500ms | 95th percentile response time |
Constraints:
- security (non-negotiable): Follow OWASP security recommendations
- security (non-negotiable): Sensitive fields must be encrypted at rest and never logged in plaintext
Autonomy
Level: supervised
Human Checkpoints:
- before modifying sensitive data fields
Escalation Triggers:
error_rate > 5consecutive_failures > 3
Verification
Invariants:
- sensitive fields are never logged in plaintext
- all data access is authenticated and authorized
- error messages never expose internal system details
Tradeoffs
| Prefer | Over | Reason |
|---|---|---|
| security | performance | authentication must prioritize preventing unauthorized access |
Coordination
Protocol: request_response
Consumes:
| Capability | From | Fallback |
|---|---|---|
login | login | fail |
Safety
| Action | Permission | Cooldown | Max Auto |
|---|---|---|---|
| rate_limited | autonomous | - | - |
| max_keys_exceeded | autonomous | - | - |
| create_key | supervised | - | - |
| rotate_key | autonomous | - | - |
| revoke_key | human_required | - | - |
| authenticate_with_key | autonomous | - | - |
| key_expired | autonomous | - | - |
| invalid_key | autonomous | - | - |