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

# Connecting a repository

> Install the GitHub App on the repositories you choose, then connect one as a project.

Scaling reaches your code through a GitHub App that you install on the repositories you choose.
Nothing is pasted: there is no access token to create, copy or rotate.
A connected repository is a project, and a project is what a session works on.

## What you need

* An account that is signed in, with an active organisation ([Quickstart](/quickstart)).
* The **admin** role in that organisation, for every step that changes something.
  A member can read what is connected and nothing else on this page.
* To be the GitHub account that owns the repository, or an owner of the GitHub organisation that does.
  Being a member of the organisation, or a collaborator on one of its repositories, is not enough.

## What the app can do

The app asks GitHub for four permissions and no others.

| Permission | Access | Why |
| - | - | - |
| Contents | Read and write | Read your code, and push a branch when you publish |
| Pull requests | Read and write | Open the pull request when you click Open PR |
| Metadata | Read | Know which repositories exist and their default branch |
| Members | Read | Confirm that the person linking the installation is an owner of the GitHub organisation |

It does not ask for the workflow permission, so it cannot change your CI files even if a session asked it to.
You choose which repositories it can see when you install it, and you can change that on GitHub at any time.

## Start the installation

```bash theme={null}
curl -s -X POST https://api.scaling.cloud/github/install/start \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json Response theme={null}
{
  "installUrl": "https://github.com/apps/scaling-cloud/installations/new?state=eyJ...",
  "state": "eyJ...",
  "expiresAt": "2026-10-04T09:40:00.000Z"
}
```

Open `installUrl` in a browser and choose the repositories.
`state` is what ties the installation you are about to make to your organisation.
It works once, for the person who asked for it, until `expiresAt`, which is ten minutes away.

## Finish the installation

GitHub asks you to authorise the App while you install it, and then sends you back with an `installation_id`, a `code` and the same `state`.
Hand all three over, signed in as the same person who started.

```bash theme={null}
curl -s -X POST https://api.scaling.cloud/github/install/complete \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"state": "eyJ...", "installationId": 61234567, "code": "a1b2c3d4e5f6a7b8c9d0"}'
```

```json Response theme={null}
{
  "installationId": 61234567,
  "accountLogin": "acme",
  "accountType": "Organization",
  "status": "active",
  "connectedAt": "2026-10-04T09:31:12.000Z"
}
```

<Note>
  The organisation the installation is linked to is the one you are signed in to. There is no
  parameter for it, and an `installation_id` on its own links nothing.
</Note>

The `code` is how GitHub tells us who made the installation.
It is exchanged once to ask GitHub who you are, and nothing from that exchange is kept.
It works once and expires quickly, so finish soon after GitHub sends you back.

An installation is linked only by the GitHub account it belongs to.
For an installation on a personal account, that is the account itself.
For an installation on a GitHub organisation, it is an owner of that organisation, the role GitHub's API calls `admin`.
Being a member of the organisation, or a collaborator on one of its repositories, is not enough.

If you are neither, nothing is linked and the answer is `404` with the code `not_found`.
That is the same answer as for an installation that does not exist.
Either way the `state` and the `code` are both used up, so start the installation again.

If GitHub does not accept the `code`, because it has expired or was already used, the answer is `400`.
Start the installation again.

## Choose a repository

List what the installation can see.

```bash theme={null}
curl -s "https://api.scaling.cloud/github/repositories?installationId=61234567" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json Response theme={null}
{
  "repositories": [
    {
      "repositoryId": 778899,
      "fullName": "acme/storefront",
      "private": true,
      "defaultBranch": "main",
      "projectId": null
    }
  ]
}
```

`projectId` is `null` until the repository is connected.
Connect it with the two ids.

```bash theme={null}
curl -s -X POST https://api.scaling.cloud/projects/connect \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"installationId": 61234567, "repositoryId": 778899}'
```

```json Response theme={null}
{
  "id": "0b0f6c1e-6a2a-4a55-9d0c-0c6a3b1f6d11",
  "githubInstallationId": 61234567,
  "githubRepositoryId": 778899,
  "defaultBranch": "main",
  "snapshotStatus": "pending",
  "snapshotCommitSha": null,
  "disabled": null,
  "createdAt": "2026-10-04T09:33:40.000Z"
}
```

Connecting a repository that is already connected answers with the project that exists.
A repository is one project however many times somebody clicks.

## See what is connected

Any member of the organisation can read both lists.

```bash theme={null}
curl -s https://api.scaling.cloud/github/installations \
  -H "Authorization: Bearer $ACCESS_TOKEN"

curl -s https://api.scaling.cloud/projects \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

An installation's `status` is `active`, `suspended` or `removed`.
A project's `disabled` is `null` while it can be used.
Otherwise it says when access was lost and why: `installation_removed`, `installation_suspended` or `repository_removed`.
A disabled project keeps its history, and no new work can read or publish its code.
Giving the app access to the repository again on GitHub, or connecting it again here, brings it back.
The projects list also carries `canStartSessions`, which is `false` while sessions cannot be started: see [Runs](/guides/starting-and-following-a-session).

## Remove the installation

```bash theme={null}
curl -s -X POST https://api.scaling.cloud/github/install/remove \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"installationId": 61234567}'
```

This uninstalls the app from the GitHub account and disables every project that was connected through it.
Uninstalling it on GitHub yourself has the same effect.

## When it fails

| Code | Status | What it means here |
| - | - | - |
| `forbidden` | 403 | You are a member and this step needs an admin |
| `invalid_request` | 400 | The `state` is not one that can be used (it expired, was already used, or was issued to someone else), or GitHub did not accept the `code` |
| `not_found` | 404 | The installation is not one your GitHub account owns or administers, or the repository is not one the installation can see |
| `conflict` | 409 | The installation is already linked to another organisation, or is suspended or removed |

An `invalid_request` on the finish step is fixed by starting again, which takes one request.
Branch on `code`, never on `message`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.