---
title: "Authenticating with API keys"
description: "How to authenticate requests, and what a key controls."
icon: "key"
---

> **For AI agents:** the complete documentation index is at [llms.txt](/llms.txt). Append `.md` to any page URL for its markdown version.

An API key authenticates every request to the platform. You send it in the **`X-Api-Key`**
header:

```http
GET /v2/things HTTP/1.1
Host: spectra.dev.aerovy.com
X-Api-Key: avy...
```

A key is bound to one **organization** and can't reach another org's data. Your organization
comes from the key, so you don't pass it in request bodies.

## Key types

A key's type is set by its prefix:

| Type | Prefix | For |
|------|--------|-----|
| **External** | `avyex` | External keys |
| **Admin** | `avyad` | Internal keys |

Admin keys are for internal use within your organization, and external keys are to be shared with other parties.

## What a key controls

Each key carries four things:

- **Scopes**: what the key can do. Each scope is an **action** (`read`, `write`, `admin`, or
  `*`) plus a **resource filter** that limits which resources the action applies to. See
  [Resource filters](/platform/resource-filters).
- **IP allow-list**: which source addresses requests can come from (below).
- **Expiry**: an optional cutoff date (below).
- **Status**: `Active`, `Expired`, or `Revoked`.

## IP allow-list (CIDRs)

You can limit a key to a set of source addresses with `allowedIpCidrs`. Each entry is one of:

| Entry | Example | Matches |
|-------|---------|---------|
| Wildcard | `*` | Any address |
| IPv4 address | `203.0.113.7` | That one address |
| IPv4 CIDR | `203.0.113.0/24` | That network range |
| IPv6 address | `2001:db8::1` | That one address |
| IPv6 CIDR | `2001:db8::/32` | That network range |

An empty list means no IP restriction. A request from an address outside the list is
rejected. Entries are validated when the key is created or updated.

## Expiry and status

- `expiresAt` is optional. If set, it must be a future time. Once it passes, the key's status
  becomes `Expired` and its requests are rejected.
- A key can be **revoked** at any time. Its status becomes `Revoked` and it stops working.
- Expired and revoked keys aren't deleted. They stay on record.

## Creating a key

Keys are minted through the platform's key API, which is an **admin operation**. A key is
created with a type, a name, its scopes, and optional IP and expiry restrictions:

```json
{
  "keyType": "External",
  "name": "depot-ingest-bot",
  "scopes": [
    { "action": "write", "resourceFilter": "PLACE/Site/<siteId>/THING/#/#" },
    { "action": "read",  "resourceFilter": "PLACE/Site/<siteId>/THING/#/#" }
  ],
  "allowedIpCidrs": ["203.0.113.0/24"],
  "expiresAt": "2027-01-01T00:00:00Z"
}
```

The full key string is shown **once**, when the key is created. Store it securely, because it
can't be retrieved again. If you lose it, revoke the key and mint a new one.

You can also create, scope, and revoke keys in the console instead of calling the key API
directly: see [API keys](/console/api-keys).
