> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instaview.sk/llms.txt
> Use this file to discover all available pages before exploring further.

# API Keys

> Complete guide to managing InstaView API keys

## Overview

API keys are the primary authentication mechanism for the InstaView API. Each key is cryptographically secure, scoped to a specific company, and has granular permissions through scopes.

## Key Architecture

### Security Features

InstaView API keys are built with enterprise-grade security:

<CardGroup cols={2}>
  <Card title="HMAC-SHA256 Hashing" icon="lock">
    Keyed hashing with a strong server-side secret; plaintext keys are never
    stored.
  </Card>

  <Card title="256-bit Entropy" icon="key">
    Cryptographically secure random key generation
  </Card>

  <Card title="Audit Logging" icon="clipboard-list">
    Complete audit trail for all key lifecycle events
  </Card>

  <Card title="Company Isolation" icon="building-lock">
    Keys are strictly scoped to prevent cross-company access
  </Card>
</CardGroup>

### Key Components

Every API key contains:

* **Prefix**: Identifies key type (`sk_` for secret keys)
* **Environment**: `live` for production, `test` for sandbox
* **Identifier**: Unique 32-character string
* **Metadata**: Name, scopes, creation date, etc.

## Creating API Keys

### Via Dashboard

<Steps>
  <Step title="Navigate to API Keys">
    Go to **Settings** → **API Keys** in your dashboard
  </Step>

  <Step title="Create New Key">
    Click **Create API Key**
  </Step>

  <Step title="Configure Details">
    ```json theme={null}
    {
      "name": "Production Integration",
      "scopes": [
        "read:jobs",
        "write:jobs",
        "read:candidates",
        "write:candidates",
        "read:interviews",
        "write:interviews"
      ],
      "expiresAt": "2025-12-31T23:59:59Z",  // Optional
      "allowedIPs": [                         // Optional
        "192.168.1.0/24",
        "10.0.0.100"
      ]
    }
    ```
  </Step>

  <Step title="Save the Key">
    **Important**: Copy your API key immediately - it will only be shown once!
  </Step>
</Steps>

### Via API (Admin Only)

```javascript theme={null}
// Requires admin JWT authentication
const response = await fetch("https://api.instaview.sk/api-keys", {
  method: "POST",
  headers: {
    Authorization: "Bearer <admin-jwt>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    companyId: "550e8400-e29b-41d4-a716-446655440000",
    name: "Production Integration",
    scopes: ["read:jobs", "write:jobs", "read:candidates", "write:candidates"],
  }),
});

const { data } = await response.json();
console.log("API Key:", data.key); // Save this securely!
```

## Scope Configuration

### Choosing the Right Scopes

Apply the principle of least privilege:

<Tabs>
  <Tab title="Analytics Dashboard">
    ```json theme={null}
    {
      "scopes": [
        "read:jobs",
        "read:candidates",
        "read:interviews",
        "read:billing"
      ]
    }
    ```

    Read-only access for dashboards and reporting.
  </Tab>

  <Tab title="Candidate Portal">
    ```json theme={null}
    {
      "scopes": [
        "read:jobs",
        "read:candidates",
        "write:candidates",
        "read:interviews"
      ]
    }
    ```

    Candidate management with limited interview access.
  </Tab>

  <Tab title="Full Integration">
    ```json theme={null}
    {
      "scopes": [
        "read:jobs",
        "write:jobs",
        "delete:jobs",
        "read:candidates",
        "write:candidates",
        "delete:candidates",
        "read:interviews",
        "write:interviews",
        "read:agents",
        "write:agents",
        "delete:agents"
      ]
    }
    ```

    Complete access for primary integrations.
  </Tab>

  <Tab title="Webhook Handler">
    ```json theme={null}
    {
      "scopes": [
        "read:interviews",
        "read:candidates"
      ]
    }
    ```

    Minimal access for processing webhook events.
  </Tab>
</Tabs>

### Scope Validation

When making API requests, scopes are validated:

```javascript theme={null}
// ✅ Success: Key has write:jobs scope
POST /jobs
Authorization: Bearer sk_live_xxx (scopes: ["write:jobs"])

// ❌ Error: Key lacks write:jobs scope
POST /jobs
Authorization: Bearer sk_live_yyy (scopes: ["read:jobs"])

// Response:
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "This API key does not have the required scope: write:jobs"
  }
}
```

## Key Types

### Regular API Keys

Standard keys associated with a single company:

```javascript theme={null}
// Automatically scoped to the key's company
GET /jobs
Authorization: Bearer sk_live_company_a_key

// Returns only jobs for Company A
```

**Use Cases**:

* Direct integrations
* Internal tools
* Customer-facing applications

### ATS Integration Keys

Special keys for multi-tenant ATS platforms:

```javascript theme={null}
// Must specify companyId for resource access
GET /jobs?companyId=company-b-uuid
Authorization: Bearer sk_live_ats_integration_key

// Can create new companies
POST /companies
Authorization: Bearer sk_live_ats_integration_key
```

**Use Cases**:

* Greenhouse, Lever, or other ATS integrations
* Multi-tenant recruitment platforms
* White-label solutions

<Info>
  ATS keys can only be created by InstaView administrators. Contact
  [support](mailto:support@instaview.sk) to request ATS integration access.
</Info>

## Advanced Configuration

### IP Allowlists

Restrict key usage to specific IP addresses or ranges:

```json theme={null}
{
  "name": "Production Integration",
  "allowedIPs": [
    "192.168.1.100", // Single IP
    "10.0.0.0/8", // CIDR notation
    "172.16.0.0/12" // Private network range
  ]
}
```

Requests from non-allowlisted IPs will be rejected:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "IP_NOT_ALLOWED",
    "message": "Request IP address not in allowlist"
  }
}
```

### Expiration Dates

Set automatic key expiration for temporary access:

```javascript theme={null}
{
  "name": "Contractor Access",
  "expiresAt": "2025-03-31T23:59:59Z",
  "scopes": ["read:candidates"]
}
```

**Best Practices**:

* Use expiration for contractor/temporary access
* Set 90-day expiration for high-privilege keys
* Receive email notifications 7 days before expiry

### Metadata and Tags

Add custom metadata for organization:

```json theme={null}
{
  "name": "Production Integration",
  "metadata": {
    "tags": ["production", "primary"],
    "environment": "us-east-1",
    "owner": "engineering@company.com",
    "project": "candidate-sync"
  }
}
```

## Key Lifecycle Management

### Monitoring Usage

Track key usage in the dashboard:

* **Last Used**: Timestamp of most recent request
* **Request Count**: Total API calls made
* **Error Rate**: Failed requests percentage
* **Top Endpoints**: Most frequently accessed endpoints

### Rotating Keys

Regular rotation improves security:

<Steps>
  <Step title="Create New Key">
    Generate a replacement key with the same scopes
  </Step>

  <Step title="Update Applications">
    Deploy the new key to all applications
  </Step>

  <Step title="Monitor Old Key">Verify traffic has moved to the new key</Step>

  <Step title="Revoke Old Key">
    Once traffic has fully migrated, revoke the old key
  </Step>
</Steps>

**Recommended Rotation Schedule**:

* High-privilege keys: Every 90 days
* Read-only keys: Every 180 days
* Compromised keys: Immediately

### Suspending Keys

Temporarily disable a key without deletion:

```javascript theme={null}
// Via dashboard or API
PATCH /api-keys/{keyId}
{
  "suspended": true,
  "suspensionReason": "Temporary maintenance"
}
```

**Use Cases**:

* Investigating suspicious activity
* Temporary service maintenance
* Debugging integration issues

### Revoking Keys

Permanently revoke a compromised key:

<Warning>
  **Revocation is permanent!** Revoked keys cannot be restored. Create a new key
  if needed.
</Warning>

```javascript theme={null}
DELETE /api-keys/{keyId}
{
  "revocationReason": "Key compromised in security incident"
}
```

All subsequent requests with the revoked key will fail:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "API_KEY_REVOKED",
    "message": "This API key has been revoked"
  }
}
```

## Security Best Practices

<AccordionGroup>
  <Accordion title="Never Expose Keys in Code" icon="code-branch">
    ❌ **Don't do this:**

    ```javascript theme={null}
    const apiKey = 'sk_live_1234567890abcdef'; // Hardcoded!
    ```

    ✅ **Do this instead:**

    ```javascript theme={null}
    const apiKey = process.env.INSTAVIEW_API_KEY;
    ```
  </Accordion>

  {" "}

  <Accordion title="Use Separate Keys per Environment" icon="layer-group">
    * **Production**: `sk_live_xxx` - **Staging**: `sk_test_xxx` -
      **Development**: `sk_test_yyy` Never use production keys in non-production
      environments.
  </Accordion>

  <Accordion title="Implement Key Rotation" icon="rotate">
    ```javascript theme={null}
    // Graceful key rotation
    const primaryKey = process.env.INSTAVIEW_PRIMARY_KEY;
    const fallbackKey = process.env.INSTAVIEW_FALLBACK_KEY;

    async function makeRequest(url, options) {
      try {
        return await fetch(url, {
          ...options,
          headers: {
            ...options.headers,
            'Authorization': `Bearer ${primaryKey}`
          }
        });
      } catch (error) {
        if (error.status === 401) {
          // Primary key failed, try fallback
          return await fetch(url, {
            ...options,
            headers: {
            ...options.headers,
            'Authorization': `Bearer ${fallbackKey}`
            }
          });
        }
        throw error;
      }
    }
    ```
  </Accordion>

  {" "}

  <Accordion title="Monitor Key Usage" icon="chart-simple">
    Set up alerts for: - Unusual request volumes - Requests from unexpected IPs -
    High error rates - After-hours usage (if unexpected)
  </Accordion>

  <Accordion title="Use IP Allowlists" icon="shield-halved">
    When your application runs from fixed infrastructure:

    ```json theme={null}
    {
      "allowedIPs": [
        "52.1.2.3",      // Production server 1
        "52.1.2.4",      // Production server 2
        "10.0.0.0/16"    // VPC network
      ]
    }
    ```

    Note: All API requests must use the `Authorization: Bearer` header with your API key.
  </Accordion>
</AccordionGroup>

## Audit Logs

All API key events are logged for security and compliance:

### Logged Events

* **Key Creation**: Who created it, when, and with what scopes
* **Authentication**: Every successful and failed authentication
* **Scope Changes**: Modifications to key permissions
* **Suspension/Revocation**: Status changes with reasons
* **Configuration Changes**: IP allowlist, expiration date updates

### Accessing Audit Logs

```javascript theme={null}
// Via dashboard: Security → Audit Logs

// Filter by key ID
GET /api-keys/{keyId}/audit-logs

// Filter by event type
GET /api-keys/{keyId}/audit-logs?eventType=authentication_failed

// Date range
GET /api-keys/{keyId}/audit-logs?from=2024-01-01&to=2024-01-31
```

### Example Audit Log Entry

```json theme={null}
{
  "id": "log_1234567890",
  "eventType": "authentication_success",
  "apiKeyId": "key_xxx",
  "timestamp": "2024-01-15T10:30:00Z",
  "ipAddress": "192.168.1.100",
  "userAgent": "MyApp/1.0",
  "endpoint": "/jobs",
  "metadata": {
    "companyId": "550e8400-e29b-41d4-a716-446655440000",
    "scopes": ["read:jobs", "write:jobs"]
  }
}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="401 Unauthorized" icon="circle-xmark">
    **Possible Causes**:

    * Invalid API key format
    * Revoked or expired key
    * Suspended key
    * Key not properly set in headers

    **Solutions**:

    * Verify key format: `sk_live_xxx` or `sk_test_xxx`
    * Check key status in dashboard
    * Ensure header is `Authorization: Bearer sk_xxx`
  </Accordion>

  {" "}

  <Accordion title="403 Forbidden" icon="ban">
    **Possible Causes**: - Insufficient scopes - IP not in allowlist - Attempting
    cross-company access **Solutions**: - Check required scope in error message -
    Verify your IP is allowlisted - Confirm you're accessing resources in your
    company
  </Accordion>

  <Accordion title="429 Rate Limited" icon="gauge-high">
    **Possible Causes**:

    * Exceeded rate limit
    * Too many requests in short time

    **Solutions**:

    * Implement exponential backoff
    * Check `Retry-After` header
    * Review [rate limiting guide](/guides/rate-limiting)
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Scopes & Permissions" icon="shield-check" href="/guides/scopes-and-permissions">
    Learn about scope-based access control
  </Card>

  <Card title="Rate Limiting" icon="gauge" href="/guides/rate-limiting">
    Understand rate limits and quotas
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle API errors gracefully
  </Card>

  <Card title="Best Practices" icon="star" href="/guides/best-practices">
    Production-ready integration patterns
  </Card>
</CardGroup>
