---
title: "API fundamentals"
description: "The conventions every endpoint shares: auth, base URLs, versioning, IDs, etc."
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.

These conventions apply across every endpoint.

## Base URLs and environments

The Aerovy Platform API is **REST over HTTPS**, returning **JSON**. The base URL is
**environment-specific**:

| Environment | Base URL |
|-------------|----------|
| Primary | `https://spectra.dev.aerovy.com` |

The **API Reference** is the authoritative reference for request and response shapes. These
docs explain the concepts.

## Authentication

Every request is authenticated with an **API key**, sent in the **`X-Api-Key`** header:

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

Your organization comes from the key, so you don't pass it in request bodies. The platform
rejects any payload whose `orgId` doesn't match your key.

A key's prefix tells you its type (`avyex` for external keys, `avyad` for internal
use), and its scopes decide what it can do. A request whose scopes don't cover the action and
resource returns **`403 Forbidden`**. For the full picture, see
[Authenticating with API keys](/platform/api-keys) and
[Resource filters](/platform/resource-filters).

## API versioning

Versioning is **in the URL path** (`/v{major}/…`). The current API is **v2**: resources,
telemetry ingest, querying, and the data-model definitions all live under the `/v2` prefix.

Examples:

- `POST /v2/thing/{thingId}/data` (ingest telemetry)
- `POST /v2/definitions/thing-types` (define a thing type)
- `GET /v2/things/{thingId}/timeseries` (read a Thing's series)

## Resource IDs

Definition resources have a **typed, prefixed id** of the form `{prefix}_{id}`:

| Resource | Prefix | Example |
|----------|--------|---------|
| Metric definition | `mdef` | `mdef_…` |
| Thing type | `tdefi` | `tdefi_…` |
| Thing type version | `tdef` | `tdef_…` |

Every other resource id is a **plain GUID** with no prefix. That includes organizations,
sites, fleets, things, and monitors:

```
b1946ac9-2492-4f1e-9a0c-0305e82c3301
```

Treat ids as **opaque strings**: match on the full value.

## Pagination

List endpoints page with two query parameters:

| Parameter | Meaning |
|-----------|---------|
| `limit` | Max items per page. |
| `continuationToken` | Opaque cursor returned by the previous page. |

The response wraps the items:

```json
{
  "items": [ /* … */ ],
  "continuationToken": "…",   // null when there are no more pages
  "hasMore": true
}
```

To page through a full result set: omit `continuationToken` on the first call, then pass
back the `continuationToken` from each response until `hasMore` is `false`. Omitting both
parameters returns the full list unpaginated.

## Errors

Errors use **RFC ProblemDetails** JSON, with Aerovy-specific detail under `extensions`:

```json
{
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
  "title": "Server Error",
  "status": 500,
  "extensions": {
    "errors": [
      { "message": "…", "code": "…", "metadata": { } }
    ]
  }
}
```

Common status codes:

| Code | Meaning |
|------|---------|
| `200 OK` | Success (read). |
| `201 Created` / `202 Accepted` | Resource created / accepted for async processing. |
| `400 Bad Request` | Malformed request or failed validation. |
| `401 Unauthorized` | Missing/invalid credential. |
| `403 Forbidden` | Authenticated, but missing the required scope. |
| `404 Not Found` | Resource doesn't exist (or isn't in your org). |
| `429 Too Many Requests` | Rate limited. |
| `500 Server Error` | Unexpected failure. |

Always read `extensions.errors[]` for the specific cause. It carries a machine-readable
`code` and a human-readable `message`.
