> ## 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.

# OpenAI (NHI)

## What You're Setting Up

You're giving Lumos read-only access to your OpenAI API Platform organization through the [Administration API](https://platform.openai.com/docs/api-reference/administration), so it can surface non-human identity inventory and risk. Setup is two values. An Admin API key, and your Organization ID. There's no policy document to write and nothing to deploy on your side.

One caution before you start: this connector reads an **API Platform organization**, the one at platform.openai.com. A ChatGPT or Codex workspace is a different tenant with its own admin surface, and its admin keys are created in the OpenAI Admin Portal instead. Those workspaces aren't covered by this integration.

**What Lumos does NOT do:**

* **No writes.** Only read capabilities are registered. Lumos can't create, modify or delete a user, service account, project, key, certificate, role or group.
* **No prompts, completions or files.** Lumos never calls the Responses, Chat Completions, Assistants, Files, Vector Stores or Batch APIs. Every call it makes is an administrative read.
* **No key secrets.** OpenAI shows a key's full value once, at creation, and never returns it from the API. The list endpoints return metadata and a redacted prefix, which is all Lumos stores.
* **No usage, spend or billing data.** The Usage and Costs endpoints are never called.
* **No audit logs.** Lumos doesn't call the Audit Logs API and doesn't need audit logging enabled on your organization.

Every authenticated request to the Administration API is logged by OpenAI for security and compliance, so you can audit exactly what ran.

***

## Prerequisites

You'll need an **Organization Owner** on the API Platform organization you want to connect. Only Owners can create Admin keys, and only an unrestricted Owner-created key can read every endpoint this connector uses. OpenAI states both limits directly.

You'll also need your **Organization ID**, the value starting with `org-` shown at [Settings > Organization > General](https://platform.openai.com/settings/organization/general).

## Step 1: Create the Admin API key

A regular project key (`sk-proj-...`) will not work. Every `/v1/organization/*` endpoint rejects it with `Missing scopes: api.management.read`, and Lumos rejects it in the connect form before you can save it.

1. Log in to [platform.openai.com](https://platform.openai.com) as an Organization Owner and go to **Settings > Organization > Admin keys**.
2. Select **Create new Admin key**.
3. Give it a **Name** you'll recognize on the key list later, such as "Lumos NHI Integration". The field is optional, and an unnamed key is hard to audit.
4. Leave **Expiration** at **Never**, or set a date and diary the rotation. The connection stops working the moment the key lapses.
5. Set **Permissions** to **Read only**.
6. Select **Create admin key** and copy the key. It starts with `sk-admin-`, and OpenAI won't show it again.

Note: Selecting **Restricted** has meaningful limitations for the integration. The per-scope picker covers users, projects, service accounts and admin keys under **Organization Administration: Read**, but it doesn't offer `api.roles.read`, `api.groups.read` or `api.mtls.read`, so roles, groups and certificates each come back `403`. The practical effect is a connection that validates, syncs users and service accounts, and then returns no roles, no groups and no entitlements.

## Step 2: Connect in Lumos

In the Lumos app, go to **Integrations > Add Integration** and search for "OpenAI (NHI)". The **Connect to OpenAI (NHI)** dialog opens with three sections, and you'll touch all three.

* **Authentication Method** is fixed to **Admin API Key**.
* Under **Capabilities and Integration Settings:**
  * Set **Scope** to **Read**. The field defaults to **Read + Write**, which grants nothing here, because the connector registers no write capability at all.
  * **Organization ID** is required. Paste the `org-` value from your organization's General settings. Lumos sends it as the `OpenAI-Organization` header on every request and stores it as the connection's tenant ID.
* Under **Authentication Credentials**, paste the `sk-admin-` key from Step 1 into **Token**, and select **Validate**. Then select **Complete**.

Two different checks run:

1. **Validate** is a format check. It confirms the token starts with `sk-admin-` and nothing else. It doesn't call OpenAI, so it passes on a well-formed key that has been revoked, belongs to another organization, or carries too few scopes.
2. **Complete** makes one real call, a single-record read of `GET /v1/organization/users`. That call needs only the most basic read permission, so a scope-restricted key clears it and then fails on every role and group read afterwards. Validate the connection by checking that roles and groups appear after the first sync.

**Connecting several organizations**

One connection covers one organization. An Admin key issued in one API Platform organization can't read another, so if you have more than one organization, repeat Step 1 and Step 2 for each and connect them separately.

***

## What Lumos reads

Every call is a `GET` against `https://api.openai.com/v1`, carrying the Admin key as a bearer token and your Organization ID in the `OpenAI-Organization` header. Pages are 100 records, walked with OpenAI's `limit` and `after` cursors.

| Endpoint | Scopes | What it's for |
| :- | :- | :- |
| `/organization/users` | `api.organization.users.read` | Organization users, their org role, and the principals that own keys |
| `/organization/users/{user_id}/roles` | `api.roles.read` | Organization role assignments |
| `/organization/projects` | `api.organization.projects.read` | Projects as resources |
| `/organization/projects/{project_id}/users` | `api.organization.projects.users.read` | Project role assignments for people |
| `/organization/projects/{project_id}/service_accounts` | `api.organization.projects.service_accounts.read` | Service accounts as non-human accounts |
| `/organization/projects/{project_id}/api_keys` | `api.organization.projects.api_keys.read` | Project keys as credentials |
| `/organization/projects/{project_id}/groups` | `api.groups.read`, `api.organization.projects.read` | Groups attached to a project |
| `/organization/admin_api_keys` | `api.organization.admin_api_keys.read` | Organization admin keys as credentials |
| `/organization/certificates` | `api.mtls.read` | mTLS certificates as credentials |
| `/organization/roles` | `api.roles.read` | Organization roles and their permissions as entitlements |
| `/organization/groups` | `api.groups.read` | Groups as resources |
| `/organization/groups/{group_id}/users` | `api.groups.read` | Group membership |
| `/organization/groups/{group_id}/roles` | `api.groups.read`, `api.roles.read` | Roles a group confers on its members |
| `/projects/{project_id}/roles` | `api.roles.read` | Project roles and their permissions as entitlements |
| `/projects/{project_id}/groups/{group_id}/roles` | `api.groups.read`, `api.roles.read` | Roles a group confers inside one project |

Each distinct permission string on an organization or project role becomes its own entitlement, typed **Organization Permission** (`ORG_PERMISSION`) or **Project Permission** (`PROJECT_PERMISSION`). The entitlement ID is the raw permission string as OpenAI returns it, so inference rules can match on it directly. Each role implies its permissions on the same resource: an organization role implies organization permissions, and a project role implies permissions in that project. A permission granted by several roles is emitted once and implied by each of them. Permission entitlements aren't assignable on their own. People and service accounts receive them through the roles they hold.

Two endpoints are allowed to fail without failing the sync. Certificates return `401 mtls_not_enabled` when your organization doesn't have the mTLS feature, and `403` when the key doesn't carry `api.mtls.read`. Either way Lumos logs it and moves on. An archived project rejects its own sub-resource endpoints with `400 project_archived`, so Lumos skips that branch rather than ending the run.

Lumos caps itself at 100 requests a minute with at most five in flight. Projects and groups are fanned out one call each, so an organization with many projects makes more round trips than a large one with few.

***

## API Limitations

These are limits in what OpenAI's API exposes, not choices Lumos made. Worth knowing before you build a process on top of the data.

**Every account reads as active.** Neither users nor service accounts expose a status upstream, so nothing arrives disabled or suspended.

**A key's own permissions are invisible.** Neither key endpoint returns the scopes a key carries, so a tightly restricted key looks exactly as broad as its owner. Lumos emits no permission-level entitlements for keys. Permission entitlements come only from the roles described in [What Lumos reads](#what-lumos-reads).

**Key status doesn't carry dormancy.** OpenAI reports no status on API keys. Lumos derives one from expiry and the owner's project access, and passes `last_used_at` through as-is rather than collapsing it into a "stale" verdict.

**No last login for people.** The users endpoint returns no activity field. Account activity is derived from the principal's API keys instead, so a person who never uses a key shows no activity even if they sign in daily.

**Certificates can't be attributed.** They carry no owner and no usage field.

**Implicit access isn't reported.** Organization Owners reach every project without an explicit project role, and Lumos emits only the explicit assignments.

**Archived projects are shells.** The project resource is emitted with `status: archived`, and no membership is enumerated inside it.

***

## Security FAQ

**Can Lumos change anything in your OpenAI organization?**

No. Only read capabilities are registered. There's no create, update or delete path in the connector, and it never issues anything but `GET` requests.

**Does Lumos read your prompts, completions or files?**

No. It calls the Administration API only. The endpoints that return content are never touched.

**Can Lumos read your API key secrets?**

No. OpenAI shows a key's value once, at creation, and never returns it from the API. What Lumos receives is metadata plus a redacted prefix, which it stores as the key's fingerprint so you can match a key seen elsewhere to this record.

**Why does the key have to be an Admin key?**

Project keys can't read the Administration API at all. OpenAI rejects them with a missing-scope error before any per-endpoint permission is evaluated.

**Why does it have to be unrestricted rather than scope-limited?**

Because roles, groups and certificates need scopes OpenAI doesn't offer in the per-scope picker. See Step 1.

**Does the Validate button prove your key works?**

Only that it's shaped like an Admin key. Validate checks the `sk-admin-` prefix locally without calling OpenAI. **Complete** makes one real read against the users endpoint.

**Do you need audit logging enabled?**

No. Lumos doesn't read audit logs.

**How are credentials handled?**

Lumos stores the Admin key you paste and sends it as a bearer token on each request. Rotating the key in OpenAI means updating it in Lumos, because the old one stops working the moment you delete it.

**How do you revoke Lumos access?**

Delete the key under **Settings > Organization > Admin keys**. The next sync fails with a `401` and Lumos surfaces a connection error. Nothing needs cleaning up on the OpenAI side, because Lumos created nothing.

**The connection saved, but there are no roles, groups or entitlements.**

The Admin key was created with **Restricted** permissions. Validation only reads users, so a restricted key passes the connect check and then collects `403`s on everything else. Create a fresh key with **Read only** permissions and update the connection with it.

**Connection rejected with "This does not look like an Admin API key."**

The key pasted doesn't start with `sk-admin-`. A project key (`sk-proj-...`) can't read the Administration API. Create an Admin key as an Organization Owner.

**A service account shows up with no project.**

Its project was archived. OpenAI refuses to enumerate membership inside an archived project, so Lumos recovers the account from the owner of its API key and has no project to attach it to. The account and its keys are still live, which is usually the point worth acting on.

**No certificates in the results.**

Expected unless your organization has mTLS enabled and the Admin key is unrestricted. Lumos logs the reason and continues.

**The Capabilities panel says "Sync: Users" and crosses out "Provisioning: Users".**

A display gap in Lumos, not a sign the connector is misconfigured. The connect dialog renders the older capability names, and the six NHI capabilities have no entry in that mapping, so none of them appear. The crossed-out provisioning row is accurate in its own way: this connector provisions nothing. Behind the panel, the connector still reads service accounts, credentials, projects, groups, roles and assignments. Entra ID (NHI) shows the same gap.
