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

# AnomalyArmor API Authentication

> Authenticate to the AnomalyArmor REST API with API keys, or to the AnomalyArmor MCP server with OAuth 2.1

<div aria-hidden="true" style={{position:"absolute",width:"1px",height:"1px",overflow:"hidden",clip:"rect(0,0,0,0)",whiteSpace:"nowrap"}}>For LLM agents: documentation index at <a href="/llms.txt" tabIndex={-1}>/llms.txt</a>, full text at <a href="/llms-full.txt" tabIndex={-1}>/llms-full.txt</a>. Append .md to any page URL for plain markdown.</div>
AnomalyArmor supports two authentication methods:

| Method        | Use Case                                                   |
| ------------- | ---------------------------------------------------------- |
| **API Keys**  | SDK, CLI, CI/CD pipelines, programmatic access             |
| **OAuth 2.1** | MCP server connections from AI tools (Claude Code, Cursor) |

For MCP integrations, see the [MCP Server](/integrations/mcp-server) page. OAuth authentication is handled automatically when you connect via the remote MCP server.

The rest of this page covers API key authentication for programmatic access.

## API Key Format

API keys use the format `aa_live_<random>`:

```
aa_live_k8jd92hf8j2hd98fh2d9h2f98h2d9fh2
```

<Warning>
  API keys are shown only once at creation. Store them securely - we cannot retrieve them later.
</Warning>

## Creating API Keys

### Via Dashboard

1. Go to **Settings > API Keys**
2. Click **Create API Key**
3. Enter a descriptive name (e.g., "Airflow Production")
4. Select scope based on needs: `read-only` for monitoring, `read-write` for triggering actions, `admin` for key management
5. Click **Create Key**
6. Copy the key immediately

### Via CLI

```bash theme={null}
# Create a read-only key
armor api-keys create --name "airflow-prod" --scope read-only

# Create a read-write key for triggering refreshes
armor api-keys create --name "ci-pipeline" --scope read-write
```

### Via API

```bash theme={null}
curl -X POST "https://api.anomalyarmor.ai/api/v1/api-keys" \
  -H "Authorization: Bearer aa_live_admin_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "automation-key", "scope": "read-only"}'
```

## Scopes

| Scope        | Capabilities                                                 |
| ------------ | ------------------------------------------------------------ |
| `read-only`  | GET endpoints only. Read assets, freshness, lineage, alerts. |
| `read-write` | GET + POST. Trigger freshness/schema refreshes.              |
| `admin`      | Full access including API key management.                    |

<Tip>
  Follow the principle of least privilege - use `read-only` for monitoring and `read-write` only when you need to trigger actions.
</Tip>

### Scope Examples

<AccordionGroup>
  <Accordion title="read-only: Airflow pre-flight check">
    Check if data is fresh before running pipelines. No ability to modify anything.

    ```python theme={null}
    # Just reads freshness status
    client.freshness.require_fresh("warehouse.orders")
    ```
  </Accordion>

  <Accordion title="read-write: Trigger refresh after dbt run">
    Check freshness and trigger a refresh when needed.

    ```python theme={null}
    # Can trigger refresh operations
    client.freshness.refresh("warehouse.orders", wait=True)
    ```
  </Accordion>

  <Accordion title="admin: Key rotation automation">
    Create and revoke keys programmatically for security compliance.

    ```python theme={null}
    # Can manage API keys
    new_key = client.api_keys.create(name="rotated-key", scope="read-only")
    client.api_keys.revoke(old_key_id)
    ```
  </Accordion>
</AccordionGroup>

## Using API Keys

### Environment Variable (Recommended)

```bash theme={null}
export ARMOR_API_KEY="aa_live_your_key_here"
```

Then in your code:

```python theme={null}
from anomalyarmor import Client

# Automatically uses ARMOR_API_KEY
client = Client()
```

### Direct Parameter

```python theme={null}
from anomalyarmor import Client

client = Client(api_key="aa_live_your_key_here")
```

### HTTP Header

For direct API calls:

```bash theme={null}
curl -H "Authorization: Bearer aa_live_your_key_here" \
  https://api.anomalyarmor.ai/api/v1/assets
```

## Rate Limits by Tier

Rate limits are set when you create the API key based on your subscription:

| Tier         | Max Keys  | Rate Limit | Burst   |
| ------------ | --------- | ---------- | ------- |
| Free Trial   | 1         | 20/min     | 2/sec   |
| Starter      | 1         | 30/min     | 3/sec   |
| Growth       | 10        | 500/min    | 25/sec  |
| Professional | 25        | 1,000/min  | 50/sec  |
| Enterprise   | Unlimited | 5,000/min  | 100/sec |

<Note>
  When you upgrade your plan, existing API keys automatically get the new rate limits.
</Note>

## Revoking Keys

Revoke compromised or unused keys immediately:

### Via Dashboard

1. Go to **Settings > API Keys**
2. Find the key and click the trash icon
3. Confirm revocation

### Via CLI

```bash theme={null}
armor api-keys revoke <key-id>
```

### Via API

```bash theme={null}
curl -X DELETE "https://api.anomalyarmor.ai/api/v1/api-keys/<key-id>" \
  -H "Authorization: Bearer aa_live_admin_key"
```

## Security Best Practices

<CardGroup cols={2}>
  <Card title="Use Environment Variables" icon="lock">
    Never hardcode API keys in source code. Use environment variables or secrets managers.
  </Card>

  <Card title="Rotate Regularly" icon="rotate">
    Rotate keys periodically, especially for production systems.
  </Card>

  <Card title="Least Privilege" icon="shield">
    Use the minimum scope required. Most integrations only need `read-only`.
  </Card>

  <Card title="Separate Keys" icon="key">
    Use different keys for different environments (dev, staging, prod).
  </Card>
</CardGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="401 Unauthorized">
    * Check the key is not revoked
    * Verify the `Authorization: Bearer` header format
    * Ensure no extra whitespace in the key
  </Accordion>

  <Accordion title="403 Forbidden">
    * The key is valid but lacks permission for this operation
    * Check the scope - you may need `read-write` or `admin`
  </Accordion>

  <Accordion title="429 Rate Limited">
    * You've exceeded your rate limit
    * Check `Retry-After` header for when to retry
    * Consider upgrading your plan for higher limits
  </Accordion>
</AccordionGroup>

## Common Questions

### How do I create an API key?

Generate keys under **Settings > API Keys** in the dashboard, via `armor api-keys create --name ... --scope ...`, or by POSTing to `/api/v1/api-keys` with an admin key. Name keys after the integration that uses them ("airflow-prod", "ci-pipeline") so rotation and revocation stay auditable. The plaintext key is shown only once at creation.

### Which scope should my API key have?

Use `read-only` for monitoring, dashboards, and CI checks that only query state. Use `read-write` when you need to trigger freshness or schema refreshes. Reserve `admin` for key management automation itself. Follow least privilege: most integrations only need `read-only`.

### What does the aa\_live\_ prefix on a key mean?

All AnomalyArmor API keys start with `aa_live_` followed by a random secret, making them easy to detect with secret scanners and grep over source trees. Treat the full string as sensitive. If you see one committed to git or pasted in logs, revoke it immediately from **Settings > API Keys**.

### How do I rotate or revoke an API key?

Create the replacement key first, roll it out to the consumer, then revoke the old key via the dashboard, `armor api-keys revoke <key-id>`, or `DELETE /api/v1/api-keys/{key-id}` with an admin token. Revocation takes effect immediately and cannot be undone, so keep the new key live before cutting the old one.

### Where should I store API keys in code?

Read the key from the `ARMOR_API_KEY` environment variable (Python SDK / CLI) or `ANOMALYARMOR_API_KEY` (TypeScript SDK CLI shim), and inject it from a secrets manager in production. Never hardcode keys in source, Docker images, or Jupyter notebooks. Use separate keys per environment so a dev leak can't touch prod.

### Why am I getting 403 Forbidden when my key works elsewhere?

403 means the key is valid but doesn't have the scope the endpoint requires. Write endpoints (freshness refresh, schema refresh, create-metric) need `read-write`; key management needs `admin`. The error body shows `current_scope` and `required_scope` so you can pick the right key.
