# API Keys

API keys provide programmatic access to Atlas services for backend services and automation scripts.

***

## What is an API Key?

An API key is a secret credential that:

* **Authenticates** backend services and scripts
* **Associates** requests with a specific project
* **Inherits** the project's team service policy
* **Enables** service-to-service communication

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                        API KEY STRUCTURE                                     │
└─────────────────────────────────────────────────────────────────────────────┘

  API Key: sk-llm-api-a1b2c3d4e5f6g7h8i9j0...
           ├─┘├──────┘├──────────────────────┘
           │    │              │
           │    │              └── Random secret (32+ characters)
           │    │
           │    └── Project identifier (optional, for readability)
           │
           └── Prefix (sk- = secret key)

  Associated Data:
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ project_id: 5ac942ba-0290-48a7-be6e-7ea58cd40b68                        │
  │ team_id: ab2785b2-b5d0-4926-92fb-00aae5ec860a                           │
  │ org_id: 4047160a-abb2-497c-bf0c-3f4ab7cb0b16                            │
  │ created_by: alice@turing.com                                            │
  │ created_at: 2025-01-15T10:30:00Z                                        │
  │ expires_at: 2026-01-15T10:30:00Z (optional)                             │
  │ status: active                                                          │
  │ last_used: 2025-01-20T14:30:00Z                                         │
  └─────────────────────────────────────────────────────────────────────────┘
```

***

## API Key Lifecycle

### Creation

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                        API KEY CREATION                                      │
└─────────────────────────────────────────────────────────────────────────────┘

  1. Admin goes to Admin → API Keys → Create

  2. Select project:
     ┌─────────────────────────────────────────────────────────────────────┐
     │ Project: LLM API                                                    │
     │ Team: Engineering (determines service access)                       │
     └─────────────────────────────────────────────────────────────────────┘

  3. Configure options:
     ┌─────────────────────────────────────────────────────────────────────┐
     │ Name: llm-api-production                                            │
     │ Description: Production API key for LLM service                     │
     │ Expires: 2026-01-15 (optional)                                      │
     └─────────────────────────────────────────────────────────────────────┘

  4. Key is generated:
     ┌─────────────────────────────────────────────────────────────────────┐
     │ ⚠️  IMPORTANT: Copy this key now. It will not be shown again!        │
     │                                                                     │
     │ sk-llm-api-a1b2c3d4e5f6g7h8i9j0klmnopqrstuvwxyz123456               │
     │                                                                     │
     │ [Copy to Clipboard]                                                 │
     └─────────────────────────────────────────────────────────────────────┘
```

### Storage (Security)

⚠️ **Critical:** The API key is hashed before storage. The original key cannot be recovered.

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                        KEY STORAGE                                           │
└─────────────────────────────────────────────────────────────────────────────┘

  Original Key (shown once):
  sk-llm-api-a1b2c3d4e5f6g7h8i9j0klmnopqrstuvwxyz123456

                    │
                    ▼ SHA-256 Hash

  Stored in Database:
  Document ID: 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069

  Contents:
  {
    "project_id": "5ac942ba-...",
    "team_id": "ab2785b2-...",
    "org_id": "4047160a-...",
    "name": "llm-api-production",
    "created_by": "alice@turing.com",
    "created_at": "2025-01-15T10:30:00Z",
    "status": "active"
  }

  Note: Original key is NOT stored anywhere
```

### Usage

```python
import httpx
import os

# Load from environment (never hardcode!)
API_KEY = os.getenv("ATLAS_API_KEY")
AUTH_GUARD_URL = os.getenv("AUTH_GUARD_URL", "https://auth-gw.atlas.turing.com")

async def call_llm_service(prompt: str):
    """Call LLM service using API key"""
    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"{AUTH_GUARD_URL}/v1/llm/generate",
            headers={
                "X-Atlas-API-Key": API_KEY,
                "Content-Type": "application/json"
            },
            json={
                "prompt": prompt,
                "max_tokens": 1000
            }
        )
        response.raise_for_status()
        return response.json()
```

### Revocation

When an API key is compromised or no longer needed:

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                        KEY REVOCATION                                        │
└─────────────────────────────────────────────────────────────────────────────┘

  1. Admin goes to Admin → API Keys

  2. Find the key by name or project

  3. Click "Revoke" → Confirm

  4. Key status changes to "revoked"

  5. All requests using this key immediately fail with 401

  Note: Revocation is immediate, no cache delay
```

***

## Authentication Flow

When a request includes an API key:

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                     API KEY AUTHENTICATION FLOW                              │
└─────────────────────────────────────────────────────────────────────────────┘

  Request:
  POST /v1/llm/generate
  X-Atlas-API-Key: sk-llm-api-a1b2c3d4...
         │
         ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ 1. HASH THE KEY                                                          │
  │    Input: sk-llm-api-a1b2c3d4...                                         │
  │    Output: 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200...    │
  └─────────────────────────────────────────────────────────────────────────┘
         │
         ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ 2. LOOKUP IN DATABASE                                                    │
  │    Query: api_keys/{hash}                                                │
  │    ❌ Not found → 401 Unauthorized: Invalid API key                       │
  └─────────────────────────────────────────────────────────────────────────┘
         │
         ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ 3. VALIDATE STATUS                                                       │
  │    Check: status == "active"                                             │
  │    ❌ status == "revoked" → 401 Unauthorized: API key revoked             │
  │    ❌ status == "expired" → 401 Unauthorized: API key expired             │
  └─────────────────────────────────────────────────────────────────────────┘
         │
         ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ 4. CHECK EXPIRATION                                                      │
  │    If expires_at is set:                                                 │
  │    ❌ now > expires_at → 401 Unauthorized: API key expired                │
  └─────────────────────────────────────────────────────────────────────────┘
         │
         ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ 5. LOAD CONTEXT                                                          │
  │    • Load project details                                                │
  │    • Load team details                                                   │
  │    • Load team service policy                                            │
  └─────────────────────────────────────────────────────────────────────────┘
         │
         ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ 6. CHECK TEAM POLICY                                                     │
  │    Target service: "llm"                                                 │
  │    Team allowed_services: ["llm", "auto-rater", "oss"]                   │
  │    Is "llm" in allowed_services?                                         │
  │    ❌ NO → 403 Forbidden: Service not allowed for team                    │
  │    ✅ YES → Continue                                                      │
  └─────────────────────────────────────────────────────────────────────────┘
         │
         ▼
  ┌─────────────────────────────────────────────────────────────────────────┐
  │ 7. INJECT HEADERS & PROXY                                                │
  │    Add: X-Project-ID, X-Team-ID, X-Org-ID, etc.                          │
  │    Forward to backend service                                            │
  │    ✅ Success                                                             │
  └─────────────────────────────────────────────────────────────────────────┘
```

***

## API Key vs JWT Token

| Aspect             | API Key                   | JWT Token                        |
| ------------------ | ------------------------- | -------------------------------- |
| **Use Case**       | Backend services, scripts | Web browsers, SPAs               |
| **Authentication** | `X-Atlas-API-Key` header  | `Authorization: Bearer` header   |
| **Expiration**     | Optional, can be years    | 24 hours                         |
| **Revocation**     | Immediate                 | Wait for expiration              |
| **Context**        | Project-level             | User-level with selected project |
| **Permissions**    | Team policy only          | Full RBAC                        |
| **Refresh**        | Generate new key          | Refresh token                    |

***

## Managing API Keys

### Admin Dashboard

Access at: `/admin/api-keys`

| Column        | Description              |
| ------------- | ------------------------ |
| **Name**      | Key identifier           |
| **Project**   | Associated project       |
| **Team**      | Associated team          |
| **Status**    | active/revoked/expired   |
| **Created**   | Creation date            |
| **Expires**   | Expiration date (if set) |
| **Last Used** | Last usage timestamp     |

### Actions

| Action     | Description                          |
| ---------- | ------------------------------------ |
| **Create** | Generate new API key                 |
| **View**   | See key details (not the key itself) |
| **Revoke** | Immediately disable key              |
| **Delete** | Remove key record                    |

***

## Security Best Practices

### Storage

| ✅ Do                           | ❌ Don't                     |
| ------------------------------ | --------------------------- |
| Store in environment variables | Hardcode in source code     |
| Use secrets manager            | Commit to git               |
| Encrypt at rest                | Store in plain text files   |
| Rotate regularly               | Share keys between services |

### Access Control

| ✅ Do                            | ❌ Don't                       |
| ------------------------------- | ----------------------------- |
| One key per service/environment | Share keys across services    |
| Set expiration dates            | Create keys that never expire |
| Revoke unused keys              | Leave old keys active         |
| Audit key usage                 | Ignore usage patterns         |

### Operations

| ✅ Do                   | ❌ Don't               |
| ---------------------- | --------------------- |
| Monitor for anomalies  | Ignore usage spikes   |
| Alert on failures      | Ignore 401 errors     |
| Rotate after incidents | Keep compromised keys |
| Document key ownership | Create orphan keys    |

***

## Code Examples

### Python

```python
import httpx
import os
from typing import Optional

class AtlasClient:
    def __init__(
        self,
        api_key: Optional[str] = None,
        base_url: Optional[str] = None
    ):
        self.api_key = api_key or os.getenv("ATLAS_API_KEY")
        self.base_url = base_url or os.getenv(
            "AUTH_GUARD_URL",
            "https://auth-gw.atlas.turing.com"
        )

        if not self.api_key:
            raise ValueError("ATLAS_API_KEY not set")

    async def call_service(
        self,
        service: str,
        endpoint: str,
        method: str = "POST",
        data: dict = None
    ):
        """Call an Atlas service through Auth Guard"""
        url = f"{self.base_url}/v1/{service}/{endpoint}"

        async with httpx.AsyncClient() as client:
            response = await client.request(
                method=method,
                url=url,
                headers={
                    "X-Atlas-API-Key": self.api_key,
                    "Content-Type": "application/json"
                },
                json=data
            )

            if response.status_code == 401:
                raise Exception("Invalid or expired API key")
            elif response.status_code == 403:
                raise Exception(f"Access denied: {response.json()}")

            response.raise_for_status()
            return response.json()

# Usage
client = AtlasClient()
result = await client.call_service(
    service="llm",
    endpoint="generate",
    data={"prompt": "Hello, world!"}
)
```

### JavaScript/TypeScript

```typescript
interface AtlasClientOptions {
  apiKey?: string;
  baseUrl?: string;
}

class AtlasClient {
  private apiKey: string;
  private baseUrl: string;

  constructor(options: AtlasClientOptions = {}) {
    this.apiKey = options.apiKey || process.env.ATLAS_API_KEY || '';
    this.baseUrl = options.baseUrl ||
      process.env.AUTH_GUARD_URL ||
      'https://auth-gw.atlas.turing.com';

    if (!this.apiKey) {
      throw new Error('ATLAS_API_KEY not set');
    }
  }

  async callService<T>(
    service: string,
    endpoint: string,
    options: {
      method?: string;
      data?: Record<string, unknown>;
    } = {}
  ): Promise<T> {
    const { method = 'POST', data } = options;
    const url = `${this.baseUrl}/v1/${service}/${endpoint}`;

    const response = await fetch(url, {
      method,
      headers: {
        'X-Atlas-API-Key': this.apiKey,
        'Content-Type': 'application/json',
      },
      body: data ? JSON.stringify(data) : undefined,
    });

    if (response.status === 401) {
      throw new Error('Invalid or expired API key');
    }

    if (response.status === 403) {
      const error = await response.json();
      throw new Error(`Access denied: ${JSON.stringify(error)}`);
    }

    if (!response.ok) {
      throw new Error(`Request failed: ${response.status}`);
    }

    return response.json();
  }
}

// Usage
const client = new AtlasClient();
const result = await client.callService('llm', 'generate', {
  data: { prompt: 'Hello, world!' }
});
```

### cURL

```bash
# Set environment variable
export ATLAS_API_KEY="sk-your-api-key-here"

# Make request
curl -X POST "https://auth-gw.atlas.turing.com/v1/llm/generate" \
  -H "X-Atlas-API-Key: $ATLAS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Hello, world!", "max_tokens": 100}'
```

***

## Troubleshooting

### "401 Unauthorized: Invalid API key"

**Causes:**

* Key doesn't exist
* Key is malformed
* Typo in key

**Solutions:**

1. Verify key is correct (check for extra spaces)
2. Generate new key if unsure
3. Check key format starts with `sk-`

### "401 Unauthorized: API key revoked"

**Causes:**

* Admin revoked the key
* Key was compromised

**Solutions:**

1. Generate new API key
2. Update your service configuration

### "401 Unauthorized: API key expired"

**Causes:**

* Key passed its expiration date

**Solutions:**

1. Generate new API key
2. Consider longer expiration or no expiration

### "403 Forbidden: Service not allowed"

**Causes:**

* Key's team doesn't have access to the service

**Solutions:**

1. Check team's service policy
2. Add service to team's allowed\_services
3. Use key from different team

### "API key not working in production"

**Causes:**

* Using dev key in production
* Environment variable not set
* Key not deployed

**Solutions:**

1. Verify correct environment
2. Check deployment configuration
3. Verify key is for production project


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gw-docs.atlas.turing.com/core-features/api_keys.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
