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

# Github

*Lumos offers three GitHub connectors, each its own integration card. This article covers **GitHub** for user and access management: sync and provisioning of organization members, outside collaborators, teams, org roles, and repository access. To authenticate as a GitHub App and sync every organization the App is installed on from one connection, see [GitHub (Multi-Auth)](/integrations/library/github-multi-auth). Its entitlement IDs differ from this connector (for example `push`/`pull` instead of `write`/`read`), so access history doesn't carry over between the two. For read-only security analysis of non-human identities (tokens, deploy keys, GitHub Apps, runners, webhooks) that feeds NHI Security Analysis, see GitHub (NHI). The connectors are independent.*

> This article applies to GitHub connections created **after September 2025** (the Domain ID under your Lumos domain shows `github_ics`). If your Domain ID shows `github.com`, your connection uses the archived pre-September-2025 setup. The main differences are noted inline below.

## After this article

You will be able to connect GitHub to Lumos to sync organization members, outside collaborators, teams, org roles, and repository access, and provision or deprovision that access from Lumos.

## Capabilities

| Functionality | Type | Sync | Provision | Description |
| :- | :- | :- | :- | :- |
| Users | Account | Yes | Yes | User account records |
| External Users | Account | Yes | No | Outside collaborator accounts |
| Organization | Permission | Yes | No | Organization membership in GitHub |
| Team Role | Permission | Yes | Yes | Teams in GitHub |
| Organizational Roles | Permission | Yes | Yes | Org roles (Administrator, Direct Member, Billing Manager) |
| Repository Role | Permission | Yes | Yes | Repository role access (`repo name / role name`, e.g. `app_name / read`) |
| Enterprise Team Role | Permission | Yes | Yes | Enterprise-level teams. Lumos assigns and removes membership through the enterprise teams API |

| Deprovisioning Action | Type | Description |
| :- | :- | :- |
| Delete | User | Removes a user from the enterprise by removing them from all enterprise organizations as a member and as an outside collaborator |

**Provisioning behavior:**

* **Create:** Lumos sends a GitHub organization invitation to the requester's email. It only succeeds if a GitHub handle is already associated with that email, and it doesn't add the user to any teams. The Create Account workflow's **Organization Role** field accepts only an org membership role: **Member** invites as `direct_member` and **Admin** invites as `admin`. **Outside Collaborator** can't be used as the create role, and selecting a Team Role saves but fails at runtime with `Expected entitlement type: ORG_ROLE, got: TEAM_ROLE`. The account shows **Pending** until the invite is accepted.
* **Assign an org role:** Lumos sets the user's organization membership. Assigning **Outside Collaborator** converts an existing org member to an outside collaborator. It can't be assigned to someone who isn't already a member.
* **Assign a repo or team role:** org owners (admins) can only be assigned as repository admins or team maintainers. Team roles only apply to organization members, not outside collaborators.
* **Assign or remove an Enterprise Team role:** Lumos manages membership through the enterprise teams API. If GitHub rejects the call with a 401 or 403, re-authorize the integration so enterprise management permissions are granted.
* **Unassign an org role:** removes the user from the organization if they're a member, or removes their outside collaborator access if they aren't.
* **Unassign a repo role:** the `read` role can't be unassigned from org members, and org owners can't be removed from repository admin.
* **Delete:** removes the user from every enterprise organization, both as a member and as an outside collaborator. The GitHub user account itself isn't deleted. Lumos marks the account **Suspended**.

Notes and caveats:

* GitHub's org RBAC roles (all-repository read/write/triage/maintain/admin) are a different concept from the Organization Role field and don't appear in that dropdown.
* GitHub API rate limits apply (5,000 requests/hour for authenticated requests). Users with private emails may not have email data available. Secret teams may not be accessible. Outside collaborators may have limited data available.
* GitHub doesn't natively expose last-login data, so activity-based deprovisioning requires supplemental data.

## Required plan and roles

* **Lumos plan:** No minimum plan requirement is documented for this integration.
* **GitHub plan:** No minimum GitHub plan is required. Provisioning works on standard GitHub organizations, and Enterprise isn't a gate.
* **GitHub role:** the connecting GitHub user must be an **Administrator or Organization Owner**. Otherwise no accounts or entitlements sync.
* **Hosting:** GitHub Cloud only. On-premise GitHub (self-hosted) instances aren't supported. GitHub Enterprise Cloud with data residency (`*.ghe.com`) is supported, and Lumos targets the regional API host (`https://api.<enterprise>.ghe.com`).
* **OAuth scopes** (requested automatically by Lumos during authorization):
  * `admin:org`: manage the organization and its teams, projects, and memberships (required for provisioning: invites, org/team role changes, member removal)
  * `repo`: full access to public and private repositories, required to read and manage repository collaborators. This scope is **not** read-only on GitHub's side. GitHub offers no finer-grained read-only alternative for repo collaborator data.
  * `read:audit_log`: read the enterprise audit log (activity monitoring)
  * `read:org`: read organization membership, teams, and roles (baseline for user/entitlement sync)
* **Read-only option:** to connect for roles and membership visibility only, grant just the read scopes (`read:org`, `read:audit_log`) and omit `admin:org`. Pulling repo-level permissions still requires `repo`, which isn't fully read-only. Avoid granting repo-level access unless you need it.

## Read-only access vs provisioning

**Is read-only possible?** Yes. Grant only the `read:org` and `read:audit_log` scopes and omit `admin:org` to connect for roles and membership visibility only, with no provisioning capability.

**What read-only requires.** During the OAuth authorization in the steps below, limit the granted scopes to `read:org` and `read:audit_log`. Pulling repository-level collaborator data still requires the `repo` scope, which GitHub does not offer in a read-only form, so leave `repo` out if you want a strictly read-only connection.

**What full provisioning requires.** The full OAuth scope set Lumos requests automatically: `admin:org` (invites, org/team role changes, member removal), plus `repo`, `read:org`, and `read:audit_log`. Follow the steps below and approve all requested scopes during authorization.

**Watch out for.** The `repo` scope is not read-only on GitHub's side, and GitHub offers no finer-grained read-only alternative for repository collaborator data, so any connection that needs repo-level permissions carries write-capable access even without `admin:org`. Provisioning a new user can only invite a GitHub handle already tied to the requester's email. Enterprise Team role changes go through the enterprise teams API and may require re-authorizing the integration so enterprise management permissions are granted.

**A missing scope fails the sync, it doesn't skip the data.** When the granted token lacks a scope a capability needs, GitHub returns a 403 and Lumos ends that capability's run with an error instead of skipping the resource and finishing. The read stages run in a fixed order: organizations, roles, teams, and members first, and repositories last. So a connection granted `read:org` and `read:audit_log` without `repo` syncs organizations, roles, teams, and members, then the entitlements and associations sync errors when it reaches the repository stage. Whatever synced before that point is kept, but the run is marked failed and repository roles never sync. Grant `repo` if you need repository data and can accept that the scope is write-capable. If you omit it for a strictly read-only connection, expect the repository stage to error on every sync while the organization, team, and member data still syncs.

## Instructions

**Phase 1: Prepare GitHub**

1. Log in to GitHub as an organization Administrator/Owner and create an **OAuth Application** for the Lumos connection.
2. Check your organization's **Third-Party Application policy**: if **No Restriction** is enabled, no action is needed. If **Access Restriction** is enabled, add/approve the new OAuth application (Lumos recommends that an admin account does this).
3. Gather your **Enterprise slug**, the slug identifying your enterprise account at `https://github.com/enterprises/`. For `https://github.com/enterprises/acme-corp`, the slug is `acme-corp`.

**Phase 2: Connect in Lumos**

1. In Lumos, go to **Settings → Integrations → GitHub** (reconnect an existing card or add a new one).
2. Enter the **Enterprise Slug** (required).
3. Optionally enable:
   * **Exclude Outside Collaborators** (default disabled). When enabled, outside collaborators aren't synced.
   * **Use SAML Identities** (default disabled). Lumos reads user identity from your SAML IdP instead of GitHub profiles, matching accounts to corporate identity (e.g. IdP email) rather than GitHub username. Requires SAML SSO configured in the GitHub organization.
4. Click **Connect** and authorize in GitHub. Lumos requests the OAuth scopes listed above automatically.

GitHub issues expiring user tokens for OAuth apps (an eight-hour access token paired with a refresh token) since August 14, 2026. Lumos stores the refresh token and renews the access token automatically, so no action is needed on your side. Connections created before that date received a non-expiring token and keep working unchanged.

*Pre-September-2025 (classic, Domain ID* `github.com`*) differences: you enter your GitHub **Organization Name** (from your URL, e.g.* `github.com/lumosapp `*→* `lumosapp`*) instead of an enterprise slug. For GitHub Enterprise you must use the underlying organization name(s), not the enterprise name. Only* `admin:org `*and* `repo `*scopes are requested. Each connection covers a single organization.*

## API Endpoints Used

<Note>
  The API endpoints this integration uses are available to logged-in Lumos customers. [Log in](/login) to view them.
</Note>

## Troubleshooting

**401 Unauthorized on connect.** Verify the OAuth Client ID and Client Secret. Verify the OAuth app is authorized in your organization (**Settings → Third-party access → OAuth Apps**). Ensure all required scopes were approved.

**"Resource not accessible by integration".** Verify the OAuth app/token has the necessary scopes (especially `read:org`) and that you aren't hitting SSO requirements.

**"SAML enforcement" error.** Authorize the OAuth app for SSO under **Settings → Third-party access → OAuth Apps → Authorize** and complete the SSO authorization flow so Lumos can access SAML data.

**Integration shows 0 users.** Usually a GitHub-side permission restriction. Confirm the connecting user is a GitHub Administrator who can view all users and org memberships (reconnect if unsure), and confirm the Lumos app is approved in your GitHub tenant.

**Users not syncing.** Verify the users are organization members (not only outside collaborators), have public or org-visible profiles, and have email addresses available.

**Most accounts show as Unmatched.** GitHub doesn't always return user email addresses, so users without public profile emails can't be auto-matched. Enable SAML authentication in the GitHub org so Lumos can match identities that authenticate via SAML (if SAML is enabled but not *required*, users who don't log in via SAML stay unmatched), or match the accounts manually.

**Account shows a personal email instead of the company email.** Lumos prioritizes the public email field on the user's GitHub profile. Match the account manually to the correct Lumos user if needed.

**Teams missing.** Verify the teams aren't secret, that the connecting user has team-admin or org-owner access, and that the teams have at least one member.

**Repository data incomplete.** Verify admin access, confirm the `repo` scope was granted, and check the repositories aren't archived.

**Sync reports an error at the repository stage.** The connection is missing the `repo` scope. Lumos reads organizations, roles, teams, and members first and repositories last, so a token without `repo` gets a 403 when it reaches the repository stage. Organization and team data still syncs, but the entitlements and associations run is marked failed and repository roles don't sync. Grant `repo` to read repository access, or accept the repository error if you're running a read-only connection on purpose.

**Create Account fails with** `Expected entitlement type: ORG_ROLE, got: TEAM_ROLE`**.** The workflow's Organization Role field was set to a Team Role. Set it to an org membership role (Member for a standard new-hire invite). Click the inner **Save** in the Editing Steps box before the outer **Save Changes**, or the setting silently reverts.

**Provisioning fails for a new user.** GitHub accounts are external accounts requiring invite acceptance, so Lumos can only invite a user whose email is already tied to an existing GitHub handle.

**Syncs fail after reauthenticating.** A "needs reauthentication" prompt can coincide with an unrelated connector-side validation bug on the external identities response, and reauthenticating alone doesn't fix it. If syncs still fail after reconnecting, contact support rather than assuming a credential problem.

**"Failed to forward OAuth callback to ICS" on connect.** This generic callback error usually masks GitHub returning no organizations for the account/token. Confirm the Lumos OAuth app is authorized for SSO on every org (if SAML is enforced), approved under third-party application access, and that you connect as an org **Owner** or member with the org selected during authorization.

**The Repository column is always empty (Classic connector).** On the Classic GitHub connector this is by design. It double-syncs repos as *Repository* (without associations) and *Repo Access* (with associations). Migrate to the ICS GitHub integration to populate it.

**Offboarding completed but the user is still active in GitHub.** Offboarding only creates removal tasks for accounts that are matched to the departing identity. A GitHub account that synced without an email address stays unmatched, so it never falls inside the offboarding scope and no GitHub removal task is dispatched to your app admins. The offboarding itself still shows as completed because every *matched* account was actioned. Check the affected accounts under **Unmatched** in the GitHub app's Accounts tab. GitHub members whose profile email is private arrive with no identifier for Lumos to correlate on, so close the gap at the source: enable SAML identity linking in the GitHub organization (see **Use SAML Identities** in Phase 2) so member identities are exposed for auto-matching, add an account-to-identity matching rule using an alternate identifier such as the GitHub username, or match the accounts manually. Matching an account after the fact does not retroactively offboard it, so for anyone who has already departed, confirm directly in GitHub that their organization access is revoked.

**Reclaiming inactive GitHub Copilot licenses.** The GitHub connector reports organization-level activity drawn from the enterprise audit log and does not expose a Copilot-specific usage signal, so Lumos cannot identify idle Copilot seats on its own. If you already produce Copilot usage data outside Lumos, you can still drive the removal through Lumos. Have your external job call a Lumos deprovisioning webhook or the Lumos REST API to trigger the removal action on whichever app grants the seat. Where Copilot access is granted through an on-prem Active Directory group rather than in GitHub, the removal runs as an Unassign Entitlement against that group through the On-Prem Agent, and the service account needs write permission on the group. Automated removals only target accounts that are matched to a Lumos identity, so if some users sit outside your connector's configured search scope, widen that scope or match them before relying on the automation.
