# POST /v1/github/installation (/docs/api/reference/github/register-installation)



<Endpoint method="POST" path="/v1/github/installation" auth="Bearer" scope="github:admin" phase="1" />

Record the GitHub App installation your user just authorized. Layers uses this binding to create short-lived tokens for cloning, analyzing, and opening PRs against any repo the install can see. One installation per organization.

The installation flow is: you send the user to a Layers install URL ([`GET /v1/github/installation/install-url`](/docs/api/reference/github/install-url)), GitHub redirects back to you with an `installation_id` in the query string, you post that ID here. One uninstall on the GitHub side revokes all access — the API does not store long-lived GitHub credentials.

<Parameters
  title="Body"
  rows="[
  { name: 'installationId', type: 'integer', required: true, description: 'The numeric `installation_id` GitHub returned on the redirect.' },
  { name: 'state', type: 'string', required: true, description: 'The CSRF `state` value GitHub round-tripped on the redirect — same string you originally sent (or that Layers generated) on `/install-url`.' },
]"
/>

The body is strict — extra keys (e.g. `orgLogin`, `note`) are rejected with `422 VALIDATION`. The org login is read from GitHub via the installation itself and returned in the response.

## Example request [#example-request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```bash
    curl https://api.layers.com/v1/github/installation \
      -H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..." \
      -H "Content-Type: application/json" \
      -d '{
        "installationId": 50291845,
        "state": "lUNN5tIRm-K3a8sBXvBQDxVOCgz2x9YK"
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    await layers.github.registerInstallation({
      installationId: 50291845,
      state: "lUNN5tIRm-K3a8sBXvBQDxVOCgz2x9YK",
    });
    ```
  </Tab>

  <Tab value="Python">
    ```python
    layers.github.register_installation(
        installation_id=50291845,
        state="lUNN5tIRm-K3a8sBXvBQDxVOCgz2x9YK",
    )
    ```
  </Tab>
</Tabs>

## Response [#response]

<Response status="201" description="Created">
  ```json
  {
    "installationId": 50291845,
    "githubAccount": {
      "login": "acme-coffee",
      "type": "Organization"
    },
    "orgLogin": "acme-coffee",
    "status": "active",
    "registeredAt": "2026-04-18T19:14:09.123456+00:00"
  }
  ```

  Re-posting the same `installationId` for an org that's already bound to it is a no-op — `registeredAt` reflects the original grant.
</Response>

## Errors [#errors]

| Status | Code              | When                                                                                                                                       |
| ------ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| 422    | `VALIDATION`      | `installationId` / `state` missing, unknown body keys supplied, or GitHub reports the installation does not exist / the App cannot see it. |
| 401    | `UNAUTHENTICATED` | Missing or invalid key.                                                                                                                    |
| 403    | `FORBIDDEN_SCOPE` | Key lacks `github:admin`.                                                                                                                  |
| 409    | `CONFLICT`        | Your organization already has a different installation bound. Detach first.                                                                |
| 429    | `RATE_LIMITED`    | Write budget exhausted.                                                                                                                    |

## See also [#see-also]

* [`GET /v1/github/installation/install-url`](/docs/api/reference/github/install-url) — create the install URL
* [`GET /v1/github/installation`](/docs/api/reference/github/get-installation) — read current binding
* [`POST /v1/projects/:id/ingest/github`](/docs/api/reference/github/ingest-github) — run the SDK-install flow
