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

# Manage API Keys

> Create, rotate, revoke, and delete API keys for programmatic access to the Naturalead API.

API keys allow external services and scripts to authenticate with the Naturalead API without a user session. Each key is scoped to an account and restricted to a specific set of RBAC permissions.

## Key concepts

<CardGroup cols={2}>
  <Card title="Scoped permissions" icon="shield-check">
    Each key is granted a subset of RBAC permissions. A key creator can only grant permissions they themselves hold.
  </Card>

  <Card title="Environment isolation" icon="toggle-left">
    Keys are either `live` or `test`. Live keys use the prefix `nl_live_` and test keys use `nl_test_`.
  </Card>

  <Card title="Shown once" icon="eye-off">
    The full key is returned only at creation (or rotation). It is hashed with SHA-256 before storage and can never be retrieved again.
  </Card>

  <Card title="Audit trail" icon="scroll-text">
    All key lifecycle events (create, revoke, rotate, delete) are logged to the audit log for SOC2 compliance.
  </Card>
</CardGroup>

## Step-by-step

<Steps>
  ### Create an API key

  Choose a descriptive name, select the permissions the key needs, and pick the environment.

  <CodeGroup>
    ```bash curl theme={null}
    curl -X POST "${API_URL}/api/api-keys" \
      -H "X-API-Key: ${API_KEY}" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "CRM lead sync",
        "scopes": ["leads:view", "leads:import", "conversations:view"],
        "environment": "live",
        "expiresAt": "2027-01-01T00:00:00Z"
      }'
    ```

    ```python Python theme={null}
    import requests

    headers = {
        "X-API-Key": API_KEY,
        "Content-Type": "application/json",
    }

    response = requests.post(
        f"{API_URL}/api/api-keys",
        headers=headers,
        json={
            "name": "CRM lead sync",
            "scopes": ["leads:view", "leads:import", "conversations:view"],
            "environment": "live",
            "expiresAt": "2027-01-01T00:00:00Z",
        },
    )

    data = response.json()
    full_key = data["fullKey"]  # Save this - shown only once!
    print(f"Key created: {data['apiKey']['keyPrefix']}...{data['apiKey']['keyHint']}")
    ```

    ```javascript Node.js theme={null}
    const response = await fetch(`${API_URL}/api/api-keys`, {
      method: "POST",
      headers: {
        "X-API-Key": API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "CRM lead sync",
        scopes: ["leads:view", "leads:import", "conversations:view"],
        environment: "live",
        expiresAt: "2027-01-01T00:00:00Z",
      }),
    });

    const data = await response.json();
    const fullKey = data.fullKey; // Save this - shown only once!
    console.log(`Key created: ${data.apiKey.keyPrefix}...${data.apiKey.keyHint}`);
    ```
  </CodeGroup>

  <Warning>
    Store the `fullKey` value immediately. It is returned only once and cannot be retrieved later. If lost, you must rotate the key to get a new one.
  </Warning>

  ### Use the API key

  Include the key in requests via the `X-API-Key` header or as a Bearer token.

  <CodeGroup>
    ```bash X-API-Key header theme={null}
    curl "${API_URL}/api/leads" \
      -H "X-API-Key: nl_live_a1b2c3d4..."
    ```

    ```bash Bearer token theme={null}
    curl "${API_URL}/api/leads" \
      -H "Authorization: Bearer nl_live_a1b2c3d4..."
    ```
  </CodeGroup>

  ### List existing keys

  View all keys for your account. Full key values are never returned -- only the prefix and last 4 characters.

  <CodeGroup>
    ```bash curl theme={null}
    curl "${API_URL}/api/api-keys" \
      -H "X-API-Key: ${API_KEY}"
    ```

    ```python Python theme={null}
    response = requests.get(
        f"{API_URL}/api/api-keys",
        headers={"X-API-Key": API_KEY},
    )

    for key in response.json():
        print(f"{key['name']}: {key['keyPrefix']}...{key['keyHint']} ({key['status']})")
    ```

    ```javascript Node.js theme={null}
    const response = await fetch(`${API_URL}/api/api-keys`, {
      headers: { "X-API-Key": API_KEY },
    });

    const keys = await response.json();
    keys.forEach((key) => {
      console.log(`${key.name}: ${key.keyPrefix}...${key.keyHint} (${key.status})`);
    });
    ```
  </CodeGroup>

  ### Rotate a key

  Rotation atomically revokes the old key and creates a new one with the same name, scopes, and environment. The old key stops working immediately.

  <CodeGroup>
    ```bash curl theme={null}
    curl -X POST "${API_URL}/api/api-keys/${KEY_ID}/rotate" \
      -H "X-API-Key: ${API_KEY}"
    ```

    ```python Python theme={null}
    response = requests.post(
        f"{API_URL}/api/api-keys/{key_id}/rotate",
        headers={"X-API-Key": API_KEY},
    )

    data = response.json()
    new_key = data["fullKey"]  # Save the new key!
    print(f"Rotated: {data['apiKey']['keyPrefix']}...{data['apiKey']['keyHint']}")
    ```

    ```javascript Node.js theme={null}
    const response = await fetch(`${API_URL}/api/api-keys/${keyId}/rotate`, {
      method: "POST",
      headers: { "X-API-Key": API_KEY },
    });

    const data = await response.json();
    const newKey = data.fullKey; // Save the new key!
    console.log(`Rotated: ${data.apiKey.keyPrefix}...${data.apiKey.keyHint}`);
    ```
  </CodeGroup>

  ### Revoke a key

  Revoking a key disables it permanently. Revoked keys remain visible for audit purposes but can be deleted.

  <CodeGroup>
    ```bash curl theme={null}
    curl -X PATCH "${API_URL}/api/api-keys/${KEY_ID}/revoke" \
      -H "X-API-Key: ${API_KEY}"
    ```

    ```python Python theme={null}
    response = requests.patch(
        f"{API_URL}/api/api-keys/{key_id}/revoke",
        headers={"X-API-Key": API_KEY},
    )
    print(response.json())
    ```

    ```javascript Node.js theme={null}
    const response = await fetch(`${API_URL}/api/api-keys/${keyId}/revoke`, {
      method: "PATCH",
      headers: { "X-API-Key": API_KEY },
    });
    console.log(await response.json());
    ```
  </CodeGroup>

  ### Delete a revoked key

  Only revoked keys can be permanently deleted. Active keys must be revoked first.

  <CodeGroup>
    ```bash curl theme={null}
    curl -X DELETE "${API_URL}/api/api-keys/${KEY_ID}" \
      -H "X-API-Key: ${API_KEY}"
    ```

    ```python Python theme={null}
    response = requests.delete(
        f"{API_URL}/api/api-keys/{key_id}",
        headers={"X-API-Key": API_KEY},
    )
    print(response.json())  # {"success": true}
    ```

    ```javascript Node.js theme={null}
    const response = await fetch(`${API_URL}/api/api-keys/${keyId}`, {
      method: "DELETE",
      headers: { "X-API-Key": API_KEY },
    });
    console.log(await response.json()); // { success: true }
    ```
  </CodeGroup>
</Steps>

## Best practices

<Note>
  Follow these recommendations to keep your API keys secure and manageable.
</Note>

* **Principle of least privilege** -- Grant only the permissions each integration actually needs. A CRM sync that only reads leads should not have `leads:import` or `campaigns:manage`.
* **Set expiration dates** -- Use `expiresAt` to enforce key rotation on a schedule. Keys without expiration remain active indefinitely.
* **Rotate regularly** -- Rotate keys at least every 90 days. Use the rotate endpoint for zero-downtime replacement.
* **Use environment separation** -- Use `test` keys during development and `live` keys in production. Never share keys across environments.
* **Never commit keys to source control** -- Store keys in environment variables or a secrets manager. If a key is accidentally exposed, revoke and rotate it immediately.
* **Monitor usage** -- Check the `lastUsedAt` field when listing keys. Keys that have not been used in a long time may be candidates for revocation.
* **Rate limits apply** -- API keys are subject to rate limiting (500 requests/minute per key, 1000 requests/minute per IP globally).

## Required permissions

| Action     | Permission required |
| ---------- | ------------------- |
| List keys  | `api_keys:view`     |
| Create key | `api_keys:manage`   |
| Revoke key | `api_keys:manage`   |
| Rotate key | `api_keys:manage`   |
| Delete key | `api_keys:manage`   |
