# Authentication

> Authenticate with the Korala API using HMAC signatures

Source: https://docs.korala.ai/guides/authentication

---

Korala secures API requests with HMAC-SHA256 signatures. Authentication is stateless, with no tokens to store or refresh.

## Overview

Each API request must include three headers:

| Header | Description | Example |
|--------|-------------|---------|
| `X-API-Key` | Your API key ID | `ak_live_abc123` |
| `X-Timestamp` | Unix timestamp in seconds | `1704067200` |
| `X-Signature` | HMAC-SHA256 signature | `a1b2c3d4...` |

## Computing the Signature

Compute the signature as an HMAC-SHA256 hash of the message below, keyed with your API secret.

### Message Format

```
{timestamp}.{METHOD}.{path}.{body}
```

Where:
- `timestamp` - The same Unix timestamp sent in the `X-Timestamp` header
- `METHOD` - The HTTP method in uppercase (GET, POST, PUT, DELETE)
- `path` - The request path including query string (e.g., `/api/v1/documents?limit=10`)
- `body` - The request body as a string (empty string for GET requests)

### Examples

    ```typescript
    import crypto from 'crypto';

    function signRequest(
      apiSecret: string,
      method: string,
      path: string,
      body: string = ''
    ): { timestamp: string; signature: string } {
      const timestamp = Math.floor(Date.now() / 1000).toString();
      const message = `${timestamp}.${method}.${path}.${body}`;

      const signature = crypto
        .createHmac('sha256', apiSecret)
        .update(message)
        .digest('hex');

      return { timestamp, signature };
    }

    // Usage
    const { timestamp, signature } = signRequest(
      'your-api-secret',
      'POST',
      '/api/v1/documents/upload-url',
      JSON.stringify({ filename: 'contract.pdf', contentType: 'application/pdf' })
    );
    ```
    ```bash
    #!/bin/bash

    API_SECRET="your-api-secret"
    METHOD="POST"
    PATH="/api/v1/documents/upload-url"
    BODY='{"filename":"contract.pdf","contentType":"application/pdf"}'

    TIMESTAMP=$(date +%s)
    MESSAGE="${TIMESTAMP}.${METHOD}.${PATH}.${BODY}"

    SIGNATURE=$(echo -n "${MESSAGE}" | openssl dgst -sha256 -hmac "${API_SECRET}" | cut -d' ' -f2)

    echo "Timestamp: ${TIMESTAMP}"
    echo "Signature: ${SIGNATURE}"
    ```

## Making Authenticated Requests

    ```typescript
    import crypto from 'crypto';

    const API_KEY = 'your-api-key-id';
    const API_SECRET = 'your-api-secret';
    const BASE_URL = 'https://api.korala.ai';

    async function apiRequest(method: string, path: string, body?: object) {
      const bodyString = body ? JSON.stringify(body) : '';
      const timestamp = Math.floor(Date.now() / 1000).toString();
      const message = `${timestamp}.${method}.${path}.${bodyString}`;

      const signature = crypto
        .createHmac('sha256', API_SECRET)
        .update(message)
        .digest('hex');

      const response = await fetch(`${BASE_URL}${path}`, {
        method,
        headers: {
          'Content-Type': 'application/json',
          'X-API-Key': API_KEY,
          'X-Timestamp': timestamp,
          'X-Signature': signature,
        },
        body: bodyString || undefined,
      });

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

      return response.json();
    }

    // Usage
    const documents = await apiRequest('GET', '/api/v1/documents');
    ```
    ```bash
    #!/bin/bash

    API_KEY="your-api-key-id"
    API_SECRET="your-api-secret"
    BASE_URL="https://api.korala.ai"

    # Function to make authenticated requests
    api_request() {
      local METHOD=$1
      local PATH=$2
      local BODY=${3:-""}

      local TIMESTAMP=$(date +%s)
      local MESSAGE="${TIMESTAMP}.${METHOD}.${PATH}.${BODY}"
      local SIGNATURE=$(echo -n "${MESSAGE}" | openssl dgst -sha256 -hmac "${API_SECRET}" | cut -d' ' -f2)

      if [ -z "$BODY" ]; then
        curl -s -X "${METHOD}" "${BASE_URL}${PATH}" \
          -H "X-API-Key: ${API_KEY}" \
          -H "X-Timestamp: ${TIMESTAMP}" \
          -H "X-Signature: ${SIGNATURE}"
      else
        curl -s -X "${METHOD}" "${BASE_URL}${PATH}" \
          -H "Content-Type: application/json" \
          -H "X-API-Key: ${API_KEY}" \
          -H "X-Timestamp: ${TIMESTAMP}" \
          -H "X-Signature: ${SIGNATURE}" \
          -d "${BODY}"
      fi
    }

    # Usage
    api_request "GET" "/api/v1/documents"
    ```

## Timestamp Validation

Korala rejects requests with timestamps older than 5 minutes to prevent replay attacks. Sync your server's clock with NTP.

## Error Responses

| Status | Error | Description |
|--------|-------|-------------|
| 401 | `missing_api_key` | `X-API-Key` header is missing |
| 401 | `missing_timestamp` | `X-Timestamp` header is missing |
| 401 | `missing_signature` | `X-Signature` header is missing |
| 401 | `invalid_api_key` | API key not found or inactive |
| 401 | `expired_timestamp` | Timestamp is older than 5 minutes |
| 401 | `invalid_signature` | Signature doesn't match |

## Security Best Practices

1. **Never expose your API secret** - Keep it server-side only
2. **Use environment variables** - Don't hardcode secrets in source code
3. **Rotate keys on a schedule** - Create new keys and deprecate old ones
4. **Use separate keys per environment** - Different keys for dev/staging/production
5. **Monitor key usage** - Review audit logs for suspicious activity
