---
title: "API Key Management"
description: "Advanced API key lifecycle management including rotation, monitoring, and enterprise key policies"
---

# API Key Management

This guide covers advanced API key management including lifecycle operations, organizational policies, monitoring, and automation for enterprise deployments.

## Key Lifecycle

```
┌─────────────────────────────────────────────────────────────────────────┐
│                        API Key Lifecycle                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Created ──> Active ──> Rotation ──> Deprecated ──> Revoked ──> Deleted│
│      │          │           │             │             │                │
│      │          │           │             │             └─ Audit retained│
│      │          │           │             └─ Grace period                │
│      │          │           └─ New key created                           │
│      │          └─ Normal operation                                      │
│      └─ Initial setup                                                    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

## Bulk Key Operations

### Creating Multiple Keys

```python
from tracertm import TraceRTMClient

client = TraceRTMClient(api_key="rtm_live_admin...")

# Create keys for multiple environments
environments = ["development", "staging", "production"]

keys = {}
for env in environments:
    key = await client.api_keys.create(
        name=f"CI/CD Pipeline - {env}",
        scopes=["read:projects", "write:items", "read:links"],
        metadata={
            "environment": env,
            "team": "platform",
            "managed_by": "terraform"
        }
    )
    keys[env] = key.key
    print(f"Created key for {env}: {key.id}")

# Store keys securely (e.g., in secrets manager)
await store_keys_in_vault(keys)
```

### Bulk Revocation

```python
# Revoke all keys for a specific team
keys = await client.api_keys.list(
    metadata_filter={"team": "departing-team"}
)

for key in keys:
    await client.api_keys.revoke(
        key.id,
        reason="Team offboarding"
    )
    print(f"Revoked: {key.name}")
```

### Bulk Rotation

```python
from datetime import datetime, timedelta

async def bulk_rotate_keys(client, filter_criteria, grace_hours=24):
    """Rotate multiple keys matching criteria."""
    keys = await client.api_keys.list(**filter_criteria)
    rotations = []

    for key in keys:
        # Create replacement
        new_key = await client.api_keys.create(
            name=f"{key.name} (rotated {datetime.now().date()})",
            scopes=key.scopes,
            metadata={
                **key.metadata,
                "rotated_from": key.id,
                "rotated_at": datetime.utcnow().isoformat()
            }
        )

        # Schedule old key deprecation
        deprecation_time = datetime.utcnow() + timedelta(hours=grace_hours)
        await client.api_keys.schedule_revocation(
            key.id,
            revoke_at=deprecation_time
        )

        rotations.append({
            "old_key_id": key.id,
            "new_key_id": new_key.id,
            "new_key": new_key.key,
            "old_key_expires": deprecation_time
        })

    return rotations

# Rotate all production keys
rotations = await bulk_rotate_keys(
    client,
    filter_criteria={"metadata_filter": {"environment": "production"}},
    grace_hours=48
)
```

## Organizational Policies

### Key Policies

```python
from tracertm.admin import OrganizationAdmin

admin = OrganizationAdmin(client)

# Set organization-wide key policies
await admin.set_key_policies({
    # Maximum key lifetime
    "max_key_lifetime_days": 90,

    # Required key naming pattern
    "name_pattern": r"^[a-z]+-[a-z]+-[a-z0-9]+$",

    # Mandatory metadata fields
    "required_metadata": ["team", "purpose", "owner_email"],

    # Scope restrictions
    "allowed_scopes": [
        "read:projects", "write:projects",
        "read:items", "write:items",
        "read:links", "write:links"
    ],

    # Prohibited scopes (require approval)
    "restricted_scopes": ["admin:*", "write:agents"],

    # IP allowlisting requirement
    "require_ip_allowlist": True,

    # MFA requirement for key creation
    "require_mfa_for_creation": True
})
```

### Compliance Rules

```python
# Set compliance rules
await admin.set_compliance_rules({
    # Automatic rotation requirement
    "auto_rotation_days": 60,

    # Warning before expiration
    "expiration_warning_days": 14,

    # Maximum unused key age
    "max_unused_days": 30,

    # Audit retention
    "audit_retention_days": 365,

    # Key approval workflow
    "approval_required_for": [
        "production_environment",
        "admin_scopes",
        "no_ip_restriction"
    ]
})
```

### Team-Level Policies

```python
# Create team with specific key policies
team = await admin.teams.create(
    name="Platform Engineering",
    key_policies={
        "max_keys_per_user": 5,
        "allowed_environments": ["development", "staging"],
        "auto_expiry_days": 30,
        "scope_allowlist": [
            "read:*",
            "write:items",
            "write:links"
        ]
    }
)
```

## Key Monitoring

### Real-Time Monitoring

```python
from tracertm.monitoring import KeyMonitor

monitor = KeyMonitor(client)

# Set up alerts
await monitor.create_alert(
    name="High Error Rate",
    condition={
        "metric": "error_rate",
        "operator": "greater_than",
        "threshold": 0.05,  # 5% error rate
        "window_minutes": 5
    },
    actions=[
        {"type": "email", "recipients": ["security@example.com"]},
        {"type": "slack", "channel": "#security-alerts"}
    ]
)

await monitor.create_alert(
    name="Unusual Usage Pattern",
    condition={
        "metric": "requests_per_minute",
        "operator": "greater_than",
        "threshold": 1000,
        "window_minutes": 1
    },
    actions=[
        {"type": "pagerduty", "service_key": "xxx"}
    ]
)
```

### Usage Analytics

```python
# Get comprehensive usage analytics
analytics = await client.api_keys.get_analytics(
    key_id="key_abc123",
    period="30d"
)

print(f"Total requests: {analytics.total_requests}")
print(f"Success rate: {analytics.success_rate}%")
print(f"Avg latency: {analytics.avg_latency_ms}ms")

# Endpoint breakdown
print("\nTop endpoints:")
for endpoint in analytics.top_endpoints[:10]:
    print(f"  {endpoint.path}: {endpoint.count} requests")

# Geographic distribution
print("\nRequests by region:")
for region in analytics.by_region:
    print(f"  {region.name}: {region.count}")

# Time-based patterns
print("\nRequests by hour:")
for hour in analytics.by_hour:
    print(f"  {hour.hour}:00 - {hour.count} requests")
```

### Health Dashboard

```python
# Get key health status
health = await client.api_keys.get_health_summary()

print("Key Health Summary")
print("=" * 40)
print(f"Total keys: {health.total_keys}")
print(f"Active keys: {health.active_keys}")
print(f"Expiring soon: {health.expiring_soon}")
print(f"Unused (30+ days): {health.unused_keys}")
print(f"Over quota: {health.over_quota}")

# Keys needing attention
if health.attention_required:
    print("\nKeys requiring attention:")
    for key in health.attention_required:
        print(f"  {key.name}: {key.issue}")
```

## Automated Management

### Terraform Integration

```hcl
# terraform/api_keys.tf

resource "tracertm_api_key" "ci_pipeline" {
  name        = "ci-pipeline-production"
  environment = "live"

  scopes = [
    "read:projects",
    "write:items",
    "read:links",
    "write:links"
  ]

  expires_at = timeadd(timestamp(), "2160h")  # 90 days

  metadata = {
    team        = "platform"
    managed_by  = "terraform"
    cost_center = "eng-platform"
  }

  ip_allowlist = [
    "10.0.0.0/8",
    "172.16.0.0/12"
  ]

  lifecycle {
    create_before_destroy = true
  }
}

# Store in AWS Secrets Manager
resource "aws_secretsmanager_secret_version" "tracertm_key" {
  secret_id = aws_secretsmanager_secret.tracertm.id
  secret_string = jsonencode({
    api_key = tracertm_api_key.ci_pipeline.key
  })
}
```

### GitHub Actions Integration

```yaml
# .github/workflows/rotate-keys.yml

name: Rotate API Keys

on:
  schedule:
    - cron: '0 0 1 * *'  # Monthly
  workflow_dispatch:

jobs:
  rotate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: pip install tracertm

      - name: Rotate API keys
        env:
          TRACERTM_ADMIN_KEY: ${{ secrets.TRACERTM_ADMIN_KEY }}
        run: |
          python scripts/rotate_keys.py

      - name: Update secrets
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Store new keys in Secrets Manager
        run: |
          python scripts/update_secrets.py
```

### Automated Cleanup

```python
import asyncio
from datetime import datetime, timedelta

async def cleanup_stale_keys(client, dry_run=True):
    """Clean up unused and expired keys."""
    keys = await client.api_keys.list()
    now = datetime.utcnow()

    stale_keys = []
    for key in keys:
        # Check if expired
        if key.expires_at and key.expires_at < now:
            stale_keys.append({"key": key, "reason": "expired"})
            continue

        # Check if unused for 30+ days
        if key.last_used_at:
            unused_days = (now - key.last_used_at).days
            if unused_days > 30:
                stale_keys.append({
                    "key": key,
                    "reason": f"unused for {unused_days} days"
                })

    print(f"Found {len(stale_keys)} stale keys")

    for item in stale_keys:
        key = item["key"]
        reason = item["reason"]
        print(f"  {key.name}: {reason}")

        if not dry_run:
            await client.api_keys.revoke(key.id, reason=reason)
            print(f"    -> Revoked")

    return stale_keys

# Run cleanup
await cleanup_stale_keys(client, dry_run=False)
```

## Webhooks for Key Events

```python
# Configure webhooks for key events
await client.webhooks.create(
    url="https://your-app.com/webhooks/tracertm",
    events=[
        "api_key.created",
        "api_key.rotated",
        "api_key.revoked",
        "api_key.expired",
        "api_key.rate_limited",
        "api_key.suspicious_activity"
    ],
    secret="whsec_..."
)
```

### Webhook Payload Example

```json
{
  "event": "api_key.rate_limited",
  "timestamp": "2024-01-15T12:00:00Z",
  "data": {
    "key_id": "key_abc123",
    "key_name": "CI Pipeline",
    "limit": 1000,
    "current_usage": 1050,
    "reset_at": "2024-01-15T13:00:00Z",
    "ip_address": "203.0.113.42",
    "endpoint": "/api/v1/items"
  }
}
```

### Webhook Handler

```python
from flask import Flask, request
import hmac
import hashlib

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_..."

@app.route("/webhooks/tracertm", methods=["POST"])
def handle_webhook():
    # Verify signature
    signature = request.headers.get("X-TraceRTM-Signature")
    payload = request.data

    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, f"sha256={expected}"):
        return "Invalid signature", 401

    # Handle event
    event = request.json

    if event["event"] == "api_key.rate_limited":
        handle_rate_limit(event["data"])
    elif event["event"] == "api_key.suspicious_activity":
        handle_suspicious_activity(event["data"])
    elif event["event"] == "api_key.expired":
        handle_expiration(event["data"])

    return "OK", 200

def handle_suspicious_activity(data):
    """Handle suspicious activity detection."""
    # Alert security team
    send_alert(
        channel="#security",
        message=f"Suspicious activity on API key: {data['key_name']}"
    )

    # Optionally auto-revoke
    if data.get("severity") == "critical":
        client.api_keys.revoke(data["key_id"], reason="auto-revoked: suspicious activity")
```

## Key Export and Backup

```python
# Export key metadata (not actual keys) for backup
export = await client.api_keys.export(
    format="json",
    include_metadata=True,
    include_usage_stats=True
)

# Save to file
with open("api_keys_backup.json", "w") as f:
    json.dump(export, f, indent=2)

# Import/recreate keys from backup
async def restore_keys_from_backup(client, backup_file):
    with open(backup_file) as f:
        backup = json.load(f)

    for key_data in backup["keys"]:
        new_key = await client.api_keys.create(
            name=key_data["name"],
            scopes=key_data["scopes"],
            metadata=key_data["metadata"]
        )
        print(f"Recreated: {key_data['name']} -> {new_key.id}")
```

## Related Documentation

- [API Keys](/docs/api-reference/authentication/api-keys) - Basic API key usage
- [OAuth 2.0](/docs/api-reference/authentication/oauth) - OAuth authentication
- [Security Best Practices](/docs/wiki/guides/security) - Security guidelines
- [Webhooks](/docs/api-reference/webhooks) - Webhook configuration

