---
title: GitHub
description: Respond to @mentions in PR and issue comment threads.
tagline: Build bots that respond to pull request and issue comment threads. Treats issues and PRs as threads, comments as messages.
package: @chat-adapter/github
---

# GitHub



## Install

<PackageInstall package="@chat-adapter/github" />

## Quick start

<Callout type="info">
  The adapter auto-detects credentials from `GITHUB_TOKEN` (or `GITHUB_APP_ID`/`GITHUB_PRIVATE_KEY`), `GITHUB_WEBHOOK_SECRET`, and `GITHUB_BOT_USERNAME`.
</Callout>

```typescript title="lib/bot.ts" lineNumbers
import { Chat } from "chat";
import { createGitHubAdapter } from "@chat-adapter/github";

const bot = new Chat({
  userName: "my-bot",
  adapters: {
    github: createGitHubAdapter(),
  },
});

bot.onNewMention(async (thread, message) => {
  await thread.post("Hello from GitHub!");
});
```

## Configuration

<TypeTable
  type={{
  token: {
    type: "string",
    description:
      "Personal Access Token. Auto-detected from `GITHUB_TOKEN`.",
  },
  appId: {
    type: "string | number",
    description: "GitHub App ID. Auto-detected from `GITHUB_APP_ID`.",
  },
  privateKey: {
    type: "string",
    description:
      "GitHub App private key (PEM). Auto-detected from `GITHUB_PRIVATE_KEY`.",
  },
  installationId: {
    type: "number",
    description:
      "Installation ID. Omit for multi-tenant setups so the adapter resolves it from each webhook.",
  },
  installationToken: {
    type: "string | (() => string | Promise<string>)",
    description:
      "Vercel Connect mode: installation access token (or a resolver invoked per API call). Skips the GitHub App JWT exchange.",
  },
  webhookSecret: {
    type: "string",
    description:
      "Webhook secret. Auto-detected from `GITHUB_WEBHOOK_SECRET`.",
  },
  webhookVerifier: {
    type: "(request, body) => unknown | Promise<unknown>",
    description:
      "Custom verifier used in place of `webhookSecret` (e.g. Vercel Connect OIDC). Takes precedence over the secret; required in Connect mode.",
  },
  userName: {
    type: "string",
    default: '"github-bot"',
    description:
      "Bot username for @mention detection. Auto-detected from `GITHUB_BOT_USERNAME`.",
  },
  apiUrl: {
    type: "string",
    description:
      "Override the GitHub API base URL (e.g. for GitHub Enterprise Server).",
  },
}}
/>

One of `token`, `appId`+`privateKey`, or `installationToken` (Vercel Connect) is
required, plus either `webhookSecret` or a `webhookVerifier`.

## Authentication

### Option A — Personal Access Token

Best for personal projects, testing, or single-repo bots.

1. Go to [Settings then Developer settings then Personal access tokens](https://github.com/settings/tokens).
2. Create a token with `repo` scope.
3. Set `GITHUB_TOKEN`.

```typescript
createGitHubAdapter({
  token: process.env.GITHUB_TOKEN!,
});
```

### Option B — GitHub App (recommended)

Better rate limits, security, and supports multiple installations.

**Create the app:**

1. Go to [Settings then Developer settings then GitHub Apps then New GitHub App](https://github.com/settings/apps/new).
2. Set **Webhook URL** to `https://your-domain.com/api/webhooks/github`.
3. Set a **Webhook secret**.
4. Permissions: Issues — Read & write, Pull requests — Read & write, Metadata — Read-only.
5. Subscribe to events: Issue comment, Pull request review comment.
6. Click **Create GitHub App** and generate a private key.

**Install the app:**

1. From your app's settings click **Install App** and choose repositories.
2. Note the **Installation ID** in the URL: `https://github.com/settings/installations/12345678`.

**Single-tenant config:**

```typescript
createGitHubAdapter({
  appId: process.env.GITHUB_APP_ID!,
  privateKey: process.env.GITHUB_PRIVATE_KEY!,
  installationId: parseInt(process.env.GITHUB_INSTALLATION_ID!, 10),
});
```

**Multi-tenant** (omit `installationId`):

```typescript
createGitHubAdapter({
  appId: process.env.GITHUB_APP_ID!,
  privateKey: process.env.GITHUB_PRIVATE_KEY!,
});
```

The adapter automatically extracts installation IDs from webhooks and caches API clients per-installation.

### Option C — Vercel Connect

Use [Vercel Connect](https://vercel.com/docs/connect) to source installation access tokens at runtime instead of storing a GitHub App private key. The `connectGitHubAdapter()` helper from [`@vercel/connect/chat`](https://www.npmjs.com/package/@vercel/connect) wires an `installationToken` resolver (skipping the App JWT exchange) and a `webhookVerifier` for Connect trigger-forwarded webhooks:

```typescript
import { createGitHubAdapter } from "@chat-adapter/github";
import { connectGitHubAdapter } from "@vercel/connect/chat";

createGitHubAdapter({
  ...connectGitHubAdapter("github/acme-github"),
  userName: "my-bot[bot]",
});
```

`installationToken` accepts a `string` or `() => string | Promise<string>` resolver invoked per API call. When `webhookVerifier` is set it takes precedence over `webhookSecret` and `GITHUB_WEBHOOK_SECRET`.

<Callout type="warn">
  In Connect mode the adapter only holds an installation token, so it can't auto-detect its own bot user id (the `/app` lookup needs the App's JWT). Without it the adapter can't recognize its own comments and will reply to itself in a loop. It learns the id from the first comment it posts, but that's in-memory only — not enough on serverless, where each webhook can hit a fresh instance. Pass `botUserId` (the numeric id of your `…[bot]` user, e.g. from `curl -s 'https://api.github.com/users/your-app%5Bbot%5D'`) so every instance knows it up front:

  ```typescript
  createGitHubAdapter({
    ...connectGitHubAdapter("github/acme-github"),
    botUserId: 12345678,
  });
  ```

  Or set the `GITHUB_BOT_USER_ID` environment variable, which the adapter auto-detects.
</Callout>

## Advanced

### Installation lookup

Resolve the GitHub App installation ID associated with a `Thread` or `Message`:

```typescript
bot.onNewMention(async (thread, message) => {
  const installationIdFromThread = await github.getInstallationId(thread);
  const installationIdFromMessage = await github.getInstallationId(
    message.threadId
  );
});
```

* **PAT mode** — returns `undefined`.
* **Single-tenant GitHub App** — returns the configured installation ID.
* **Multi-tenant** — only succeeds after the adapter has received a webhook for that repository and cached the mapping. Use a persistent state adapter so the mapping survives restarts.

### Direct API client

Access the underlying [Octokit](https://github.com/octokit/octokit.js) instance via `.octokit`:

```typescript
const github = bot.getAdapter("github").octokit;
const { data: pulls } = await github.rest.pulls.list({
  owner: "vercel",
  repo: "chat",
  state: "open",
});
```

PAT and single-tenant App modes return the same client anywhere. Multi-tenant mode requires webhook-handler context — calling `.octokit` outside a handler throws.

> The previous `.client` getter still works as a deprecated alias for `.octokit`.

### Webhook setup

For repository or organization webhooks:

1. **Settings** then **Webhooks** then **Add webhook**.
2. Set payload URL to `https://your-domain.com/api/webhooks/github`.
3. Set **Content type** to `application/json` (the default `application/x-www-form-urlencoded` does not work).
4. Set **Secret** to match `webhookSecret`.
5. Select events: Issue comments, Pull request review comments.

GitHub App webhooks are configured during app creation — make sure to select `application/json`.

### Thread model

| Type            | Context              | Thread ID format                                  |
| --------------- | -------------------- | ------------------------------------------------- |
| PR-level        | PR Conversation tab  | `github:{owner}/{repo}:{prNumber}`                |
| Review comments | PR Files Changed tab | `github:{owner}/{repo}:{prNumber}:rc:{commentId}` |
| Issue comments  | Issue thread         | `github:{owner}/{repo}:issue:{issueNumber}`       |

### Reactions

| SDK emoji     | GitHub reaction |
| ------------- | --------------- |
| `thumbs_up`   | +1              |
| `thumbs_down` | -1              |
| `laugh`       | laugh           |
| `confused`    | confused        |
| `heart`       | heart           |
| `hooray`      | hooray          |
| `rocket`      | rocket          |
| `eyes`        | eyes            |

## Feature support

<FeatureSupport />

## Resources

* [Ship a GitHub code review bot with Hono and Redis](https://vercel.com/kb/guide/ship-a-github-code-review-bot-with-hono-and-redis?utm_source=chat-sdk_site\&utm_medium=docs\&utm_campaign=adapter-github\&utm_content=ship-a-github-code-review-bot-with-hono-and-redis) — Walks through building a GitHub bot that reviews pull requests on demand. When a user @mentions the bot on a PR, Chat SDK picks up the mention, spins up a Vercel Sandbox with the repo cloned, and uses AI SDK to analyze the diff.

See all guides and templates on the [resources](/resources?utm_source=chat-sdk_site\&utm_medium=docs\&utm_campaign=adapter-github\&utm_content=resources) page.
