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

# API

The Integrations API lets you connect and manage your app integrations programmatically - the same connections you'd otherwise set up by hand in the Lumos UI. Use it to wire up integrations from a script, a CI pipeline, or an infrastructure-as-code tool.

## Overview

An integration is a connection between Lumos and one of your apps (for example Okta or Google Workspace). Once connected, Lumos syncs that app's accounts and entitlements so you can review and manage access. With this API you can browse the catalog of integrations you can connect, discover what each one needs to connect, connect one by supplying its credentials, trigger a sync on demand, reconnect one to rotate its credentials, and disconnect one.

To see the integrations you've already connected (for example to reconcile state from Terraform), use the Apps API: GET /v1/apps?connection\_source=API lists the integrations you connected through this API.

## Before you start

You need:

* **An API token.** Create one in the Lumos UI under **Settings → API Tokens**. The token is shown once - copy it somewhere safe. Examples below read it from a `LUMOS_ACCESS_TOKEN` environment variable.
* **Permission to manage integrations.** A token acts as the user who created it, so that user must be an admin with permission to connect and remove integrations. Without it, calls return `403`.
* **The base URL** `https://api.lumos.com`. All paths below are relative to it.

Some apps (those that use OAuth or the Merge widget) can't be fully connected from the API alone - the API call starts the connection and you finish authorizing it in the Lumos UI. See [Connect an integration](#2-connect-an-integration).

## Authentication

Send your token as a bearer token in the `Authorization` header on every request:

```bash theme={null}
curl -s https://api.lumos.com/v1/integrations \
  -H "Authorization: Bearer $LUMOS_ACCESS_TOKEN"
```

A request with a missing or invalid token returns `401`.

## Concept-to-endpoint map

Use this table to jump from what you're trying to do to the right endpoint.

| I want to... | Endpoint | Notes |
| - | - | - |
| Browse integrations I can connect | `GET /v1/integrations` | Catalog metadata, keyed by `app_class_id`. Paginated; filter with `?query=`. |
| Look up one connectable integration | `GET /v1/integrations/{app_class_id}` | Catalog metadata for a single `app_class_id`. |
| See what an integration needs to`connect` | `GET /v1/integrations/{app_class_id}/connection-schema` | JSON schema of the auth and settings to supply. |
| Connect a new integration | `POST /v1/apps` | Put the app's credentials in `auth`. Returns the integration's `id`. |
| Update credentials or fields | `PUT /v1/apps/{id}` | "Reconnect" (also triggers a sync). Uses the `id` from connect. |
| Trigger a sync on demand | `POST /v1/apps/{id}/sync` | Runs a full sync. `409` while one is already running. |
| Disconnect an integration | `DELETE /v1/apps/{id}` | Removes stored credentials. Uses the `id` from connect. |
| List integrations connected via this API | `GET /v1/apps?connection_source=API` | The Terraform read path; returns each connected integration in the `App` shape. |

## Core workflow

### 1. Discover what you can connect

Before connecting anything, find the integration's `app_class_id` and learn what it needs to connect.

**Browse or search the catalog** of connectable integrations:

```bash theme={null}
curl -s "https://api.lumos.com/v1/integrations?query=okta" \
  -H "Authorization: Bearer $LUMOS_API_TOKEN"
```

```json theme={null}
{
  "items": [
    {
      "app_class_id": "okta.com",
      "name": "Okta",
      "description": "Okta is a management platform that secures critical resources from cloud to ground for workforce and customers.",
      "category": "IT & Security",
      "logo_url": "https://..."
    }
  ],
  "total": 1,
  "page": 1,
  "size": 50,
  "pages": 1
}
```

The `app_class_id` is the canonical identifier you'll pass when connecting. The list is paginated (`?page=`, `?size=`) and `?query=` filters by identifier or name.

**Check the connection schema** to see exactly which credentials (`auth`) and configuration (`settings`) the integration takes — and where each value goes:

```bash theme={null}
curl -s "https://api.lumos.com/v1/integrations/okta.com/connection-schema" \
  -H "Authorization: Bearer $LUMOS_API_TOKEN"
```

```json theme={null}
{
  "app_class_id": "okta.com",
  "auth_schema": {
    "type": "object",
    "properties": {
      "api_key": { "type": "string", "title": "API key", "writeOnly": true }
    },
    "required": ["api_key"]
  },
  "settings_schema": {
    "type": "object",
    "properties": {
      "app_instance_identifier": { "type": "string", "title": "Domain" }
    },
    "required": ["app_instance_identifier"]
  }
}
```

Read it as: put every key from `auth_schema` in the request's `auth` object (secret fields are marked `writeOnly`) and every key from `settings_schema` in `settings`. Here, Okta takes an `api_key` credential and its tenant domain as `settings.app_instance_identifier`. Other integrations use different keys — some name the tenant differently (e.g. `sub`), some derive it from the credentials and need no setting at all, and some nest `auth` per authentication method when the integration supports several. An empty `auth_schema` means no credentials are supplied up front: the connection is completed via browser consent in the Lumos UI.

### 2. Connect an integration

**Precondition:** the `app_class_id` and connection schema from step 1, and valid credentials for the app.

Send the identifier in `app_class_id`, credentials in `auth`, and non-secret configuration in `settings` — shaped exactly as the connection schema describes:

```bash theme={null}
curl -s -X POST https://api.lumos.com/v1/apps \
  -H "Authorization: Bearer $LUMOS_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "app_class_id": "okta.com",
    "auth": { "api_key": "your-okta-api-token" },
    "settings": { "app_instance_identifier": "your-org.okta.com" }
  }'
```

A `201 Created` response returns the connected integration in the same `App` shape as the rest of the API. When the credentials are complete, the first sync starts automatically:

```json theme={null}
{
  "id": "7d3b1c2e-8f4a-4c1d-9b2e-1a2b3c4d5e6f",
  "app_class_id": "okta.com",
  "instance_id": "your-org.okta.com",
  "user_friendly_label": "Okta",
  "disconnected": false
}
```

*(Response trimmed to the relevant fields — the full* `App `*shape also carries catalog metadata such as* `category`*,* `description`*, and* `logo_url`*.)*

Save the `id` - you'll use it to look up, sync, reconnect, or disconnect the integration. `instance_id` is the tenant/instance Lumos resolved for the connection — either the value you configured or one derived from the credentials.

**OAuth and Merge apps connect in two steps.** For those, the API call starts the connection but doesn't complete it — finish authorizing the connection in the Lumos UI. No sync runs until you do.

### 3. Configure its fields

You set an integration's fields when you connect it: credentials in `auth` and non-secret configuration in `settings` (see the [field reference](#field-reference)).

**Precondition:** the integration `id` from step 2.

To change those fields later — for example to rotate an API key — send the new values to `PUT /v1/apps/{id}`. The `app_class_id` must match the existing integration:

```bash theme={null}
curl -s -X PUT https://api.lumos.com/v1/apps/$INTEGRATION_ID \
  -H "Authorization: Bearer $LUMOS_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "app_class_id": "okta.com",
    "auth": { "api_key": "your-new-okta-api-token" }
  }'
```

A `200 OK` response returns the integration in the same shape as connect. If a sync is already running for the integration, the reconnect is throttled with `429` and a `Retry-After` header (seconds) — wait that long and retry.

### 4. Trigger a sync

Lumos starts a sync automatically whenever a connect or reconnect completes successfully. To run a sync on demand afterwards — for example from a scheduled job, or right after Terraform connects the integration — call `POST /v1/apps/{id}/sync` with the `sync_type` you want. Today the only supported value is `full_sync` (a full re-pull of accounts, groups, and entitlements):

```bash theme={null}
curl -s -X POST https://api.lumos.com/v1/apps/$INTEGRATION_ID/sync \
  -H "Authorization: Bearer $LUMOS_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "sync_type": "full_sync" }'
```

**Precondition:** the integration is connected (or its last sync failed — retrying after a failure is allowed).

The sync runs asynchronously, so a `202 Accepted` confirms it was launched — it does not wait for the sync to finish:

```json theme={null}
{
  "app_id": "7d3b1c2e-8f4a-4c1d-9b2e-1a2b3c4d5e6f",
  "sync_type": "full_sync",
  "status": "SYNCING"
}
```

Only one sync runs at a time. A few cases to handle:

* `409 Conflict `**— a sync is already in progress.** Wait for the running sync to finish, then retry. A `409` is also returned when the integration isn't connected yet, or when its first sync is deferred pending an admin review in the Lumos UI.
* `422 Unprocessable Entity` — manual/CSV apps can't be synced this way.

### 5. List what you've connected

The Apps API is the read path. Fetch a single integration by the `id` you saved from connect, or list everything you connected through this API. Responses never return credentials.

```bash theme={null}
curl -s "https://api.lumos.com/v1/apps/$INTEGRATION_ID" \
  -H "Authorization: Bearer $LUMOS_API_TOKEN"
```

Each integration is returned in the same `App` shape as connect (trimmed here):

```json theme={null}
{
  "id": "7d3b1c2e-8f4a-4c1d-9b2e-1a2b3c4d5e6f",
  "app_class_id": "okta.com",
  "instance_id": "your-org.okta.com",
  "disconnected": false
}
```

`disconnected: false` means the integration is connected (its credentials are stored); after you disconnect it, it reads `true`.

To list every integration you connected through this API at once, use the `connection_source` filter:

```bash theme={null}
curl -s "https://api.lumos.com/v1/apps?connection_source=API" \
  -H "Authorization: Bearer $LUMOS_API_TOKEN"
```

### 6. Disconnect

Disconnecting removes the integration's stored credentials.

**Precondition:** the integration `id`.

```bash theme={null}
curl -s -X DELETE https://api.lumos.com/v1/apps/$INTEGRATION_ID \
  -H "Authorization: Bearer $LUMOS_API_TOKEN"
```

A `200 OK` response confirms the disconnect — the app is no longer connected but remains available to reconnect later:

```json theme={null}
{
  "id": "7d3b1c2e-8f4a-4c1d-9b2e-1a2b3c4d5e6f",
  "app_class_id": "okta.com",
  "disconnected": true
}
```

## Field reference

These are the fields you provide when connecting or reconnecting an integration (`POST` / `PUT`). Discover the exact keys each integration accepts via `GET /v1/integrations/{app_class_id}/connection-schema`.

| Field | What it is | Required | Constraints |
| - | - | - | - |
| `app_class_id` | Identifier of the app to connect, e.g. `okta.com`. | Required | Must be an app available to your domain. An unknown value returns `400`; omitting it returns `422`. |
| `auth` | Object holding the app's credentials. **All secrets go here.** | Required for most apps | Keys depend on the app (for example `api_key`, or `client_id` and `client_secret`); the connection schema lists them. May be empty for apps you finish authorizing in the UI. Wrong or missing credentials return `400`; credentials the app rejects return `502`. |
| `settings` | Object of non-secret configuration (for example region, host, or tenant/instance identifier). | Optional | Keys depend on the app; the connection schema lists them. Apps whose tenant is set manually take it here (for Okta, as `settings.app_instance_identifier`); others derive it from the credentials. Defaults to empty. |
| `version` | Pins the integration to a specific version. | Optional | Leave unset to use the default, which is correct for almost all integrations. |

### What you get back

Responses never include credentials. Connect/reconnect return the connected integration in the same `App` shape as the rest of the API — most relevantly `id`, `app_class_id`, `instance_id` (the resolved tenant/instance), and `disconnected` — plus catalog metadata. The Apps API (`GET /v1/apps`) returns the same shape.

## Handling errors

Errors return the relevant HTTP status and a JSON body with a human-readable `detail`, plus machine-readable `type` and `code` fields your automation can branch on:

```json theme={null}
{
  "detail": "Integration datadog.com not found.",
  "type": "not_found",
  "code": "not_found",
  "errors": []
}
```

| Status | Cause | What to do |
| - | - | - |
| `400 Bad Request` | Missing or invalid credentials, an unknown `app_class_id`, or a field the app doesn't support. | Fix the request body; confirm the required `auth` keys via the connection schema. |
| `401 Unauthorized` | Missing or invalid API token. | Send a valid `Authorization: Bearer` token. |
| `403 Forbidden` | Your user can't manage integrations. | Use a token for an admin with integration permissions. |
| `404 Not Found` | No integration with that `id` (or `app_class_id`) in your domain. | Check the `id` from the connect or list response. |
| `409 Conflict` | An integration for this app instance already exists; or (on sync) a sync is already running or the app isn't in a syncable state. | Reconnect the existing integration instead of connecting a new one; for syncs, wait for the running sync to finish and retry. |
| `422 Unprocessable Entity` | The request body failed validation (for example `app_class_id` is missing), or the app can't be synced via the API (manual/CSV). | Provide all required fields in the correct shape. |
| `429 Too Many Requests` | A sync is already running while you reconnect. | Wait and retry; honor the `Retry-After` header (seconds). |
| `502 Bad Gateway` | The app rejected the credentials, or the sync failed. | Verify the credentials with the provider, then retry. |

## Next steps

* Browse the integrations you can connect with `GET /v1/integrations`, and check each one's `connection-schema` before connecting.
* Audit what you've connected through this API with `GET /v1/apps?connection_source=API`.
* Rotate credentials on a schedule by reconnecting with `PUT /v1/apps/{id}`.
* See the full API reference at [API reference](/reference/lumos-api).

<br />
