# Build against the API

> Generate a client for the LightHouse API from its OpenAPI document, authenticate with a machine key, and make a first call in Python or TypeScript.

The LightHouse API is described by an OpenAPI 3 document. Atlantic does not publish client libraries for the API. To call it from your own code, generate a client from the OpenAPI document, or call the routes directly with any HTTP client.

## The OpenAPI document

| Document   | Address                                                              | Contents                                                                                                        |
| ---------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Public     | [`https://docs.atlanticagenticlabs.com/openapi.json`](/openapi.json) | The client API, as documented in the [API reference](/api/reference/). No credential is required to read it     |
| Per tenant | `https://<tenant id>.atlanticagenticlabs.com/api/v1/openapi.json`    | The document served by your tenant’s LightHouse. Reading it requires the same authentication as any other route |

The public document matches the release these docs describe. Regenerate your client when LightHouse is upgraded; the [changelog](/changelog/) lists each release’s changes.

## Authentication

Code that runs unattended uses a **machine key** (`aalcp-`), issued under **Settings → Keys**, and your tenant’s **Access service token**. Every request carries three headers:

| Header                    | Value                                    |
| ------------------------- | ---------------------------------------- |
| `Authorization`           | `Bearer aalcp-…`                         |
| `CF-Access-Client-Id`     | The Access service token’s client ID     |
| `CF-Access-Client-Secret` | The Access service token’s client secret |

A machine key reads. It cannot approve, decide or pay. See [Machine keys](/api/authentication/#machine-keys) and [The Access service token at the edge](/api/authentication/#the-access-service-token-at-the-edge). Keep the key and the service token in your secret store or masked CI variables, not in source code.

## A direct call

The examples read `AAL_TENANT`, `AAL_KEY`, `CF_ACCESS_CLIENT_ID` and `CF_ACCESS_CLIENT_SECRET` from the environment and call `GET /api/v1/me`.

**curl**

```bash
curl "https://$AAL_TENANT.atlanticagenticlabs.com/api/v1/me" \
  -H "Authorization: Bearer $AAL_KEY" \
  -H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
  -H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET"
```

**Python**

```python
import os

import httpx

headers = {
    "Authorization": f"Bearer {os.environ['AAL_KEY']}",
    "CF-Access-Client-Id": os.environ["CF_ACCESS_CLIENT_ID"],
    "CF-Access-Client-Secret": os.environ["CF_ACCESS_CLIENT_SECRET"],
}
base = f"https://{os.environ['AAL_TENANT']}.atlanticagenticlabs.com"

with httpx.Client(base_url=base, headers=headers, timeout=30) as client:
    response = client.get("/api/v1/me")
    response.raise_for_status()
    print(response.json())
```

**TypeScript**

```ts
const base = `https://${process.env.AAL_TENANT}.atlanticagenticlabs.com`;
const headers = {
  Authorization: `Bearer ${process.env.AAL_KEY}`,
  "CF-Access-Client-Id": process.env.CF_ACCESS_CLIENT_ID!,
  "CF-Access-Client-Secret": process.env.CF_ACCESS_CLIENT_SECRET!,
};

const response = await fetch(`${base}/api/v1/me`, { headers });
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
console.log(await response.json());
```

If Cloudflare Access answers instead of LightHouse (a redirect to a sign-in page), the Access service token is missing or wrong.

## Generate a client

Any OpenAPI 3 generator can produce a client. The two below are maintained open-source projects; Atlantic does not maintain either.

### Python: openapi-python-client

[openapi-python-client](https://github.com/openapi-generators/openapi-python-client) generates a typed Python package, built on `httpx`, with one function per operation.

```bash
pipx install openapi-python-client
openapi-python-client generate --url https://docs.atlanticagenticlabs.com/openapi.json
```

The generated package’s `AuthenticatedClient` sends the machine key as `Authorization: Bearer`. Pass the Access service token as two more headers when the client is created. The package’s own README lists its modules and functions.

### TypeScript: openapi-typescript and openapi-fetch

[openapi-typescript](https://openapi-ts.dev/) generates the API’s types; [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) is a small `fetch` wrapper that checks paths, parameters and responses against them.

```bash
npm install openapi-fetch
npm install --save-dev openapi-typescript
npx openapi-typescript https://docs.atlanticagenticlabs.com/openapi.json -o lighthouse.d.ts
```

```ts
import createClient from "openapi-fetch";
import type { paths } from "./lighthouse";

const lighthouse = createClient<paths>({
  baseUrl: `https://${process.env.AAL_TENANT}.atlanticagenticlabs.com`,
  headers: {
    Authorization: `Bearer ${process.env.AAL_KEY}`,
    "CF-Access-Client-Id": process.env.CF_ACCESS_CLIENT_ID!,
    "CF-Access-Client-Secret": process.env.CF_ACCESS_CLIENT_SECRET!,
  },
});

const { data, error } = await lighthouse.GET("/api/v1/me");
if (error) throw new Error(JSON.stringify(error));
console.log(data);
```

## Behaviour a client must implement

A generated client sends requests and parses responses. The following behaviour is the caller’s responsibility:

| Behaviour   | Requirement                                                                                                                                                         | Reference                                 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| Retries     | Retry only `429`, `502`, `503` and network failures. Honour `Retry-After`, or `RateLimit-Reset` on a `429` without it; otherwise back off exponentially with jitter | [Rate limits](/api/rate-limits/#retrying) |
| Idempotency | Send an `Idempotency-Key` on every write, the same on each retry of that write                                                                                      | [Idempotency](/api/idempotency/)          |
| Pagination  | Follow `next` until it is absent                                                                                                                                    | [Pagination](/api/pagination/)            |
| Errors      | Branch on the fixed `error` code in the response body, not on the message text                                                                                      | [Errors](/api/errors/)                    |

## From a terminal or CI

The [CLI](/cli/) covers the same reads from a shell and in CI, with retries, pagination and JSON output built in.

Source: https://docs.atlanticagenticlabs.com/api/build-against-the-api/
