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

# GitHub

> Connect GitHub to Serval to run repository, team, and enterprise workflows and manage GitHub Team access - authenticated entirely through GitHub Apps.

## About GitHub

Serval's GitHub integration connects your GitHub Organization, User account, or Enterprise to Serval so workflows can read repositories, manage teams, and call the GitHub API on your behalf. It authenticates exclusively through GitHub Apps - never personal access tokens. You choose between two Serval-managed apps with different access tiers, or bring a GitHub App owned by your own organization.

**Authentication:** GitHub App installation only. Pick a Serval-managed app - **Full Admin Access** or **Code Contributor Access** - or choose **Use Your Own GitHub App**. The bring-your-own path offers two options: let Serval walk you through installing the app (**Install with Serval**), or connect an app your GitHub organization admin already installed out of band (**App already installed**) by pasting its credentials. Workflow API calls never rely on a stored long-lived token: a fresh, short-lived installation access token is minted for every call.

**Data sync:** Workflow API calls run on demand. For Access Management, GitHub Teams are ingested on a fixed schedule every 16 hours, always as a full sync (GitHub does not support delta sync).

## What the GitHub integration enables

| Capability                                  | Description                                                                                                                                                                                                                     |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Get GitHub Repository Info                  | Retrieve detailed information about a GitHub repository. Runs without approval by default.                                                                                                                                      |
| Get GitHub File Contents                    | Retrieve the contents of a file from a GitHub repository. Runs without approval by default.                                                                                                                                     |
| List GitHub Repository Contents             | List files and directories in a GitHub repository path. Runs without approval by default.                                                                                                                                       |
| List GitHub Repositories                    | List all repositories in a GitHub organization. Runs without approval by default.                                                                                                                                               |
| List GitHub Teams                           | List all teams in a GitHub organization - the only approved way to fetch teams. Runs without approval by default.                                                                                                               |
| Add GitHub User to Team                     | Add a user to a GitHub team, with validation that the team exists. Requires the user's GitHub username, not an email address. Requires installer approval by default.                                                           |
| List GitHub Enterprise Roles                | List custom enterprise roles defined on your GitHub enterprise. Requires an enterprise-scoped install. Runs without approval by default.                                                                                        |
| Update GitHub Enterprise Administrator Role | Set an enterprise member's administrator role to Owner or Billing Manager. Requires an enterprise-scoped install. Requires installer approval by default.                                                                       |
| Generic REST API requests                   | Typed access to any endpoint in GitHub's official REST API from custom workflows, limited by the installed app's permissions.                                                                                                   |
| Enterprise REST API requests                | A dedicated action for enterprise-only GitHub endpoints, intended for enterprise-scoped installs.                                                                                                                               |
| GraphQL requests                            | Run queries and mutations against GitHub's GraphQL API; GraphQL-layer errors are surfaced as failures rather than silently dropped.                                                                                             |
| Access Management for GitHub Teams          | GitHub Teams are ingested as resources (full sync every 16 hours) with **Member** and **Maintainer** entitlements. Users can request team access through Serval, and membership is provisioned and deprovisioned automatically. |

Anything defined in the [GitHub API](https://docs.github.com/en/rest) can be accessed through Serval.

## Get your credentials

What you need depends on which connect option you choose in the **Connect GitHub** modal.

<Tabs>
  <Tab title="Serval-managed app (Full Admin or Code Contributor)">
    Nothing to create. Both **Full Admin Access** ("Read contents of selected repos, open pull requests, create and manage repos and teams, invite users") and **Code Contributor Access** ("Read contents of selected repos, open pull requests, and comment on issues") use GitHub Apps that Serval manages - no credentials are required.

    <Note>
      The install must be performed by (or approved by) a **GitHub Organization Owner**. If you're not an owner, your install lands in a pending state until an owner approves it in GitHub - see Gotchas below.
    </Note>
  </Tab>

  <Tab title="Use Your Own GitHub App">
    You'll need a GitHub App owned by your organization, plus a client secret and a private key generated for it. Follow GitHub's app settings pages in [GitHub Developer Settings](https://github.com/settings/apps).

    <Steps>
      <Step title="Open or create your GitHub App">
        Go to [GitHub Developer Settings](https://github.com/settings/apps) and create a new GitHub App, or open an existing one. For an org-owned app, use the org's **Settings → Developer settings → GitHub Apps**.
      </Step>

      <Step title="Set the Callback URL">
        Copy the **Callback URL** shown in Serval's connect form (it has a Copy button) and paste it into your app's **Callback URL** field.
      </Step>

      <Step title="Enable OAuth during installation">
        Check **"Request user authorization (OAuth) during installation"**.

        <Warning>
          Serval needs this setting to mint installation tokens and complete the install handshake. Don't skip it.
        </Warning>
      </Step>

      <Step title="Generate a client secret">
        Under **Client secrets**, click **Generate a new client secret** and copy it.

        <Note>
          GitHub only shows the client secret once. Copy it before leaving the page.
        </Note>
      </Step>

      <Step title="Generate a private key">
        Under **Private keys**, click **Generate a private key** and save the downloaded `.pem` file. You'll paste its full contents into Serval, including the BEGIN/END lines.
      </Step>

      <Step title="Note the app's identifiers">
        From the app's **About** section, note the numeric **App ID**, the **Client ID** (starts with "Iv"), and the **Public link** (your app's public page on GitHub). These plus the client secret and `.pem` contents fill Serval's form.
      </Step>
    </Steps>

    <Tip>
      Use the key GitHub generates for you, unmodified. Serval's validation only accepts the RSA private key format GitHub's **Generate a private key** button produces (the file begins with `-----BEGIN RSA PRIVATE KEY-----`) - keys converted to other formats will be rejected.
    </Tip>
  </Tab>

  <Tab title="Use Your Own GitHub App (already installed)">
    Use this option when your security process requires the app to be installed **separately by an authorized organization admin** after the app and its permissions have been reviewed - not inline during Serval's connect flow. Your admin creates the app, configures permissions, transfers it to the organization, and installs it entirely on GitHub; Serval never triggers the install and no OAuth callback is involved.

    Because Serval authenticates purely as the app (a signed JWT minted from the private key), this path needs **no OAuth client secret and no "Request user authorization (OAuth) during installation"** setting. You only collect three values, all readable by an org owner from the app's settings and installed-apps pages:

    <Steps>
      <Step title="Install the reviewed app on your organization">
        An organization owner installs the GitHub App on the org with the approved repository access, following your normal review/approval process.
      </Step>

      <Step title="Generate a private key">
        In the app's **Settings → Private keys**, click **Generate a private key** and save the downloaded `.pem` file. Only an admin of the app can generate it, and GitHub shows it only at generation time.
      </Step>

      <Step title="Find the App ID and Installation ID">
        The numeric **App ID** is in the app's **About** section. For the **Installation ID**, open the org's **Installed GitHub Apps** settings and click **Configure** on the app - the number at the end of the URL (`.../installations/<ID>`) is the installation ID.
      </Step>
    </Steps>

    <Note>
      The **private key is the sensitive credential** on this path - it can mint installation tokens for the app, so treat it as a secret and prefer a dedicated key you can rotate or revoke independently. Possession of the key is also what authorizes the connection (there is no OAuth ownership check), which cleanly separates the admin's install action on GitHub from the connect action in Serval.
    </Note>
  </Tab>
</Tabs>

## Connect in Serval

<Tabs>
  <Tab title="Serval-managed app">
    <Steps>
      <Step title="Open the Connect GitHub modal">
        Click **Connect** on GitHub in Serval. The modal asks you to "Choose the access level that best suits your needs" - pick **Full Admin Access** or **Code Contributor Access**.
      </Step>

      <Step title="Install the app on GitHub">
        Serval opens GitHub's install screen. Pick the target organization (or user or enterprise account), then choose **all repositories** or **only selected repositories**, and click **Install & Authorize**.
      </Step>

      <Step title="Return to Serval">
        GitHub sends you back to Serval, which records the installation and names the connection after your org or user login (or the enterprise slug for Enterprise installs). No credentials are ever entered.
      </Step>
    </Steps>

    <Note>
      Repository selection and access tier are fixed at install time. Repositories can be adjusted later in GitHub's installed-apps settings; switching tiers requires uninstalling in GitHub and reconnecting with the other tier.
    </Note>
  </Tab>

  <Tab title="Use Your Own GitHub App">
    Choosing **Use Your Own GitHub App** ("Connect with a GitHub App owned by your organization.") switches the modal to a bring-your-own form with a toggle at the top: **Install with Serval** (the guided two-step form below) and **App already installed** (paste credentials for an app your admin installed out of band - see the next tab). **Install with Serval** is the default.

    <Steps>
      <Step title="Step 1 - Set up your GitHub App">
        Copy the read-only **Callback URL** (the Copy button shows "Copied" for 2 seconds) into your GitHub App's Callback URL setting, and make sure **"Request user authorization (OAuth) during installation"** is checked.
      </Step>

      <Step title="Step 2 - Enter your app's credentials">
        * **Display name** (optional) - "A friendly name shown in Serval - you choose this." Placeholder: "My Company GitHub App".
        * **GitHub App public link** (required) - accepts the full Public link, a scheme-less form, or a bare slug; the form shows `Detected slug: [slug]` once parsed. If it can't be parsed: `Couldn't read the GitHub App slug from that link. Paste your app's Public link, e.g. https://github.com/apps/your-app-name.` Backend errors: `appSlug is required`, `appSlug must be a valid GitHub App slug`.
        * **App ID** (required) - must be numeric. Errors: `appId is required`, `appId must be numeric`.
        * **Client ID** (required) - starts with "Iv". Error: `clientId is required`.
        * **Client secret** (required, password field) - "The value you copied in step 4 (GitHub won't show it again)." Error: `clientSecret is required`.
        * **Private key PEM** (required, multi-line) - paste the full `.pem` contents including the BEGIN/END lines. Serval validates the key by signing a test token. Errors: `privateKey is required`, `privateKey must be a valid GitHub App private key: [error details]`.
      </Step>

      <Step title="Connect and install">
        Click **Connect**. Serval validates your entries, then sends you to GitHub to install your app - pick the account and repositories and click **Install & Authorize**. Serval then verifies that the installed app matches the App ID you entered; if they don't match, the connection fails with `Failed to create integration` - double-check that the Public link and App ID belong to the same GitHub App.
      </Step>
    </Steps>

    <Warning>
      Complete the GitHub install promptly - your submitted credentials are held for only **10 minutes**. If the window lapses you'll see `Invalid or expired OAuth state` and must resubmit the form.
    </Warning>

    <Note>
      **Reconnecting:** the form prefills Display name, GitHub App public link, App ID, and Client ID from the saved connection, but **Client secret and Private key PEM always start blank and are required again** - there is no keep-existing-secret option. Have the `.pem` file and a client secret on hand (or generate new ones in GitHub) before reconnecting.
    </Note>
  </Tab>

  <Tab title="Use Your Own GitHub App (already installed)">
    In the **Connect GitHub** modal, choose **Use Your Own GitHub App**, then switch the toggle to **App already installed**. There is no callback URL to copy and no redirect to GitHub - the app is already installed, so you just enter its credentials and connect.

    <Steps>
      <Step title="Enter your app's credentials">
        * **Display name** (optional) - a friendly label shown in Serval; you choose it.
        * **App ID** (required) - must be numeric. Errors: `appId is required`, `appId must be numeric`.
        * **Installation ID** (required) - must be numeric. Errors: `installationId is required`, `installationId must be numeric`.
        * **GitHub App public link** (optional) - stored for reference; when provided it must parse to a valid slug (`appSlug must be a valid GitHub App slug`).
        * **Private key PEM** (required, multi-line) - paste the full `.pem` contents including the BEGIN/END lines. Errors: `privateKey is required`, `privateKey must be a valid GitHub App private key: [error details]`.
      </Step>

      <Step title="Connect">
        Click **Connect**. Serval authenticates as the app to confirm the installation is real and belongs to the App ID you entered, records the connection, and names it after your organization or user login. If verification fails you'll see `Could not verify the GitHub App installation. Double-check the App ID, Installation ID, and private key, and confirm the app is installed on your organization.`
      </Step>
    </Steps>

    <Note>
      **Reconnecting:** Display name, GitHub App public link, App ID, and Installation ID are prefilled from the saved connection, but the **Private key PEM starts blank and is required again**. The modal reopens on this option automatically when the saved connection was created this way.
    </Note>

    <Warning>
      This path is for **organization or user** installs. An enterprise-scoped app is rejected with `this app is installed on a GitHub Enterprise. Use the GitHub Enterprise integration to connect an enterprise-scoped app` - use the GitHub Enterprise integration instead.
    </Warning>
  </Tab>
</Tabs>

## Verifying the connection

Serval runs three health checks against a connected GitHub integration:

1. **Test GitHub App Installation** - verifies a token can be minted and works, by making a small test request (a sample of accessible repositories for org/user installs, or a single enterprise team for Enterprise installs).
   * Success: `GitHub App installation for [name] is properly configured and functional` - or, for enterprise installs, `GitHub Enterprise App installation for [name] is properly configured and functional`
   * Failure: `GitHub App installation token for [name] is invalid or not working.` followed by a status-specific hint:
     * 401 - `The GitHub App installation token is invalid or expired. Please reinstall the GitHub App.`
     * 403 - `The GitHub App may not have the required permissions. Check the app's permission configuration.`
     * 404 - `The GitHub App installation was not found. It may have been uninstalled or it may be awaiting approval from an admin of [name]. Visit https://github.com/organizations/[name]/settings/installations for more info.`
     * 500/502/503 - `GitHub returned a server error. This is likely a temporary issue with GitHub's API - please try again later.`

2. **List GitHub Teams** - confirms the integration can list teams in the connected scope (org teams, or enterprise teams for Enterprise installs).
   * Success: `Successfully listed [number] teams in [name]` - or, for enterprise installs, `Successfully listed [number] enterprise teams in [name]`
   * Failure: `Unable to list GitHub teams in [name].` followed by the same 401/403/server-error hints; the generic 404 variant reads `The requested resource was not found. This may indicate missing permissions or incorrect configuration.`

3. **List GitHub Repositories** - confirms the integration can list accessible repositories for the org. Enterprise installs skip this check and report it as passing.
   * Success: `Successfully listed [number] repositories for [name]` - enterprise installs instead report `Not applicable: this check only validates org/user installs. Enterprise install ([name]) covered by test-installation-token.`
   * Failure: `Unable to list GitHub repositories for [name].` followed by the same 401/403/404 (generic)/server-error hints.

<Tip>
  If all three checks are green but a specific workflow still fails with a permission error, the cause is almost always install-time scoping: the access tier you chose (Full Admin vs Code Contributor), the repository selection (all vs selected), or - on Enterprise installs - workflows that require an organization install. Check the app's settings in GitHub's installed-apps page, and see the gotchas below.
</Tip>

## Gotchas and troubleshooting

<AccordionGroup>
  <Accordion title="Non-owner installs land in pending approval">
    If the person connecting isn't a GitHub Organization Owner, GitHub treats the install as a request and Serval creates a pending connection. Until an owner approves the app in GitHub, every API call fails with `GitHub installation is pending approval`. Once approved, the connection completes automatically - Serval matches the pending record even if the approval finishes outside the original browser session, returning the approver to Serval (or showing `GitHub installation completed. You can close this window.` when no return destination was recorded). If more than one pending install exists for the same GitHub target, completion errors with `multiple pending installations found for this GitHub target - please keep only one`.
  </Accordion>

  <Accordion title="Connecting an app installed out of band (no inline install)">
    If your security process requires an authorized org admin to install the reviewed app **separately** rather than inline during a vendor's connect flow, use **Use Your Own GitHub App → App already installed**. Serval authenticates as the app via a signed JWT, so this path needs no OAuth callback and no client secret - only the App ID, Installation ID, and private key. There is no 10-minute credential window and no pending-approval handshake: the admin installs the app on GitHub first, then anyone with the credentials connects it in Serval. Because there is no OAuth ownership check on this path, **possession of the private key is the authorization** - treat it as a secret and use a dedicated, rotatable key.
  </Accordion>

  <Accordion title="Custom-app installs have a 10-minute approval window">
    For **Use Your Own GitHub App**, submitted credentials are held for only 10 minutes. If your org requires admin approval, the connection rejects with `GitHub App installation is pending approval. Ask a GitHub organization owner to approve the app, then reconnect it in Serval.` - your credentials are deliberately kept so an immediate retry works, but only until the 10 minutes expire, after which you'll see `Invalid or expired OAuth state` and must resubmit the form.
  </Accordion>

  <Accordion title="Reconnecting a custom app requires re-entering both secrets">
    On reconnect, Display name, GitHub App public link, App ID, and Client ID are prefilled, but **Client secret** and **Private key PEM** always start blank and are hard-required - submit is blocked and blanks are rejected with `clientSecret is required` / `privateKey is required`. There is no keep-existing-secret path. Have the `.pem` file and a client secret ready (or generate fresh ones in GitHub) before you start.
  </Accordion>

  <Accordion title="Enterprise installs have no org or repo permissions">
    An Enterprise-scoped install is tied to the enterprise, not an organization. Workflows that inherently target an organization fail with `This workflow requires a GitHub organization install, but the connected install is enterprise-scoped ([name]).` Team ingestion returns an empty set for enterprise installs, the repository health check is reported as not applicable, and enterprise-only endpoints require the dedicated enterprise API action.
  </Accordion>

  <Accordion title="Use GitHub usernames, not email addresses">
    Team ingestion never creates new Serval users, because GitHub emails may be missing or personal. The **Add GitHub User to Team** workflow rejects anything that looks like an email address with `Invalid GitHub username: '[username]'. Please provide a GitHub username (not an email address). GitHub usernames typically contain only letters, numbers, and hyphens.` Access Management provisioning is likewise instructed never to guess: its input is annotated "The target user's GitHub username. Do not make guesses about the user's username. Ask the user for their GitHub username."
  </Accordion>

  <Accordion title="Uninstalling the app in GitHub breaks the connection immediately">
    Tokens are minted fresh for every API call, so an uninstall (or an unapproved install) surfaces right away as `The GitHub App installation was not found. It may have been uninstalled or it may be awaiting approval from an admin of [name]. Visit https://github.com/organizations/[name]/settings/installations for more info.`
  </Accordion>

  <Accordion title="Repository selection and access tier are fixed at install time">
    Which repositories Serval can read is decided on GitHub's install screen (all vs selected) and managed afterward in GitHub's installed-apps settings, not in Serval. The two Serval-managed tiers are separate GitHub Apps - switching tiers means uninstalling the current app in GitHub and reconnecting with the other one.
  </Accordion>

  <Accordion title="Teams sync is a full refresh every 16 hours">
    GitHub does not support incremental sync, so every scheduled run re-enumerates all teams, their repositories, and memberships. Newly created GitHub teams appear after the next scheduled sync or a manual resync.
  </Accordion>
</AccordionGroup>

***

Need help? Contact **[support@serval.com](mailto:support@serval.com)** for assistance with your GitHub integration.
