---
title: "GitLab"
description: "Use gitlab.com and self-managed GitLab: issues, merge requests, to-dos, pipelines, releases and webhooks."
canonical_url: "https://forges.link/providers/gitlab"
---
# GitLab

> Use gitlab.com and self-managed GitLab: issues, merge requests, to-dos, pipelines, releases and webhooks.

The GitLab provider works with gitlab.com and self-managed GitLab. It can read and write issues, merge requests and commit comments, and it also covers to-dos, pipelines and jobs, commit statuses, releases, vulnerabilities and the webhooks of projects and groups. Merge requests have `kind: 'pull_request'`.

<provider-header provider="gitlab">
<table>
<thead>
  <tr>
    <th>
      Detail
    </th>
    
    <th>
      Value
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Import
    </td>
    
    <td>
      <code>
        forges/gitlab
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Factories
    </td>
    
    <td>
      <code>
        gitlab()
      </code>
      
      , <code>
        gitlabLite()
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Auth
    </td>
    
    <td>
      <code>
        token
      </code>
      
      , <code>
        anonymous
      </code>
    </td>
  </tr>
</tbody>
</table>
</provider-header>

## Authentication

The provider accepts a personal, project, group or OAuth access token:

```ts
import { gitlab } from 'forges/gitlab'

const forge = gitlab({
  auth: { type: 'token', token: process.env.GITLAB_TOKEN! },
}).create()
```

Project and group access tokens act as bot users, so they are the right choice when you need a credential that isn't tied to a person.

If you leave out `auth`, the provider makes anonymous reads of public data. GitLab serves notes, labels, milestones, members, commit statuses, commit and code search and vulnerabilities only to signed-in users, even on public projects. An anonymous provider therefore reports comments and events, `repos.labels`, `repos.milestones`, `repos.collaborators`, `repos.permissionFor`, `checks.list`, `search.commits`, `search.code` and `securityAlerts` as unsupported. GitLab.com also shows full profiles only to signed-in users, so an anonymous `users.get()` returns the login, ID, name, avatar and URL, without `bio`, `createdAt` or follower counts.

## Self-managed instances

To use a self-managed instance, set `baseUrl` to the root of the instance. The provider appends `/api/v4` itself:

```ts
gitlab({ baseUrl: 'https://gitlab.example.com', auth })
```

Webhook delivery logs and redelivery require GitLab 17.0 or later. If you know the version of the instance, pass it as `instanceVersion`. If you don't, `refreshCapabilities()` reads it from `/version`.

## Nested groups

GitLab projects can live in nested groups, so `RepoRef.owner` holds the full namespace path. A project in a subgroup has `owner: 'acme/platform'` and `name: 'widgets'`.

`repos.byOwner()` lists the projects of a group, or of a user when no group has that path. It leaves out projects that other groups share with the group, and it includes the projects of subgroups when you pass `subgroups: true`. GitLab doesn't record pushes, so `sort: 'pushed'` sorts by last activity, which also counts new issues and merge requests, and adds a `sort_unsupported` warning.

## Contributors

GitLab counts commits per author email, so `repos.contributors()` returns one contributor per email, with a `name` and an `email` but no `actor`.

## Templates

`repos.templates()` returns the description templates GitLab offers in the project, including those it inherits from its groups and the instance. The listing has names only, so each body costs one more request. A template has no `path`, since GitLab doesn't say which project it lives in.

## Notifications are to-dos

On GitLab, `notifications.list()` reads the to-dos of the user. A to-do is either pending or done, and there is no separate read state, which affects how the notification operations behave:

- `markRead()` rejects with `UnsupportedOperationError`, because there is nothing to mark as read.
- `markDone()` marks the to-do as done.
- `unsubscribe()` requires `{ thread }`, because GitLab can't look up a to-do by its ID.

## Event kinds from system notes

GitLab reports changes to labels, states and assignments as English text in system notes, and the provider works out the kind of event by parsing that text. This is why `capabilities.eventKinds` is `'heuristic'`. When the provider doesn't recognise a note, the result is an event with `kind: 'other'`, and the `kindRaw` and `payload` of the event keep the original note.

## Approvals as reviews

GitLab has approvals instead of reviews, so the provider presents them as reviews. `threads.reviews()` returns one review for each approver, with the ID `approval:<username>`. `createReview({ event: 'approve' })` approves the merge request. `createReview({ event: 'comment', body })` opens a resolvable discussion on it instead, and the comment of the review it returns carries the discussion as its `thread`. For any other input, such as inline comments, it rejects with `UnsupportedOperationError`.

`approve()` and `createReview({ event: 'approve' })` resolve with your approval, from the approvals in the response. When the merge request has more than one approver, the provider reads the signed-in account to tell which approval is yours, once per provider.

Comments on a merge request are read by discussion, so a comment in a resolvable discussion has a `thread` whose ID `resolveReviewThread()` takes. `replyToReviewComment()` posts a note to that discussion, so pass it a comment that has a `thread`, such as one from `comments()`. A page holds whole discussions, so it can hold more comments than `perPage`.

When the user isn't allowed to approve, GitLab responds with `401`. The provider raises this response as `InsufficientScopeError`.

## Merge request stats

GitLab reports the number of changed files on a single merge request, but not the lines added or removed or the number of commits, so `thread.stats` holds only `changedFileCount`. Past the diff limit of the instance, GitLab reports a capped count such as `1000+` instead of the number, and `thread.stats` is absent.

## Releases

GitLab releases have no draft or prerelease state, so every release is published as soon as you create it. `releases.create()` and `releases.update()` reject `draft: true` and `prerelease: true` with `UnsupportedOperationError` before they send a request. If the tag doesn't exist yet, `releases.create()` needs `target`, the branch or commit to tag.

GitLab keys a release by its tag, so `releases.update()` can't change the tag or the commit of a release. It rejects a different `tag`, or any `target`, with `UnsupportedOperationError`.

The assets of a GitLab release are links to arbitrary URLs and not files that GitLab hosts, so `releases.downloadAsset()` isn't available. To download an asset, fetch `asset.url` yourself.

## Committing files

GitLab can't check the head of a branch as it commits, so with `expectedHead`, `contents.commitFiles()` reads the branch first and rejects with `BranchMovedError` when it has moved. A commit that lands between the two requests isn't caught.

## Search

`search.commits()` uses GitLab's Advanced Search. GitLab doesn't report whether an instance has Advanced Search enabled, so the capability is `'experimental'`. On an instance without it, the search returns an empty page.

Commit search accepts free text only. If you pass `author`, `committer`, `since`, `until` or `sort`, the result has a `filter_unsupported` warning. `search.threads()` has no `involves` filter, and it adds the same warning when you pass one.

`search.code()` with a `repo` works on every GitLab tier. Pass `owner` instead to search a group, or neither to search the whole instance. Those two need Advanced Search or exact code search, so Premium or Ultimate. Without either, GitLab responds with `400`. GitLab.com has instance-wide code search switched off, so there it's a `403`. A personal namespace isn't a group, so an `owner` that names a user gets a `404`. A group or instance result names its project only by ID, so the provider looks up the projects of each page in one GraphQL request and remembers them. A project the token can't read leaves its results out of the page, with a `record_unreachable` warning.

GitLab returns one result per excerpt, not per file, and without Advanced Search it lists a file again when its name matches as well as its contents. The provider merges the results for a file in a page into one match and drops repeated excerpts, so `fragments` can hold several, each with the line it starts on, and `raw` is the list of GitLab results that the match keeps. When the excerpts of a file span two pages, the file comes back on both.

## Activity feeds

`repos.events()` reads the events of a project, and `users.events()` the events of a user. A user's events name each project only by ID, so `users.events()` looks up the projects of each page in one GraphQL request and remembers them, as code search does. An event in a project the credential can't read has no `repo` and no `thread`, and the page has a `record_unreachable` warning.

A push in a feed names only its last commit, so `detail.commits` holds that commit, while `detail.commitCount` counts every commit of the push.

## Webhooks

GitLab doesn't sign the body of a delivery. Instead, it sends the secret token of the hook unchanged in the `X-Gitlab-Token` header, and the provider compares that header with your `webhookSecret`:

```ts
const forge = gitlab({
  webhookSecret: process.env.GITLAB_WEBHOOK_SECRET,
}).create()

const events = await forge.webhooks.ingest({
  headers: request.headers,
  body: await request.text(),
})
```

The name of the event is in `X-Gitlab-Event`, and the ID of the delivery is in `X-Gitlab-Event-UUID`.

When an update to an issue or merge request changes labels, assignees or reviewers, it becomes one event for each label or person that was added or removed. If there is more than one event, the ID of each is the ID of the delivery followed by `:` and the index of the event.

If you create a hook with `webhooks.create()`, `nativeEvents` takes the names of GitLab's event flags, such as `issues_events`.

## Organisation teams

GitLab has no teams, so a subgroup stands in for one. `orgs.teams()` reads `GET /groups/{id}/descendant_groups` and lists every subgroup of a group, nested ones included, which GitLab serves for a public group without a credential. A nested subgroup's `parent` is the subgroup it belongs to. `orgs.teamMembers()` reads `GET /groups/{id}/members`, which lists the subgroup's direct members but not those it inherits from the groups above it, and needs a token. It addresses the subgroup by the ref's `id`. The `slug` is the subgroup's path below the group, so for a ref with only the group `acme` and the slug `platform/web`, it reads `GET /groups/acme%2Fplatform%2Fweb/members`.

## Capabilities

<provider-capabilities provider="gitlab">
<table>
<thead>
  <tr>
    <th>
      Capability
    </th>
    
    <th>
      Support
    </th>
    
    <th>
      Without credentials
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        experimental
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sources.poll
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sources.webhook
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sources.subscribe
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.get
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        users.get
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        users.me
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        users.sponsorable
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        users.events
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.list
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.byOwner
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.labels
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.createLabel
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.milestones
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.collaborators
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.contributors
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.contributorStats
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.templates
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.permissionFor
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.addCollaborator
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.assignableUsers
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.reviewerCandidates
      </code>
    </td>
    
    <td>
      emulated
    </td>
    
    <td>
      emulated
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        repos.events
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        orgs.teams
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        orgs.teamMembers
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.get
      </code>
    </td>
    
    <td>
      issue, PR, commit
    </td>
    
    <td>
      issue, PR, commit
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.list
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      issue, PR
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.getMany
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.events
      </code>
    </td>
    
    <td>
      issue, PR, commit
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        comments.list
      </code>
    </td>
    
    <td>
      issue, PR, commit
    </td>
    
    <td>
      commit
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        comments.get
      </code>
    </td>
    
    <td>
      issue, PR, commit (emulated)
    </td>
    
    <td>
      commit (unverified)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        comments.edit
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        comments.delete
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reactions.list
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      issue, PR
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.list
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.markRead
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.markDone
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.unsubscribe
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.markAllRead
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.markAllDone
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.unreadCount
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.comment
      </code>
    </td>
    
    <td>
      issue, PR, commit
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.upsertComment
      </code>
    </td>
    
    <td>
      issue (emulated), PR (emulated)
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.close
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.reopen
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.create
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.update
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.setLabels
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.addLabels
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.removeLabels
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.setMilestone
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.react
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.setAssignees
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.requestReview
      </code>
    </td>
    
    <td>
      PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.merge
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.approveAndMerge
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.transfer
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.markDuplicate
      </code>
    </td>
    
    <td>
      emulated
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        subscriptions.get
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        subscriptions.set
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        installations
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        checks.thread
      </code>
    </td>
    
    <td>
      PR
    </td>
    
    <td>
      PR
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        checks.list
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        checks.get
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        checks.report
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        checks.rerun
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.runs
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.run
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.jobs
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.job
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      emulated
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.log
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.file
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.tree
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.branches
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.tags
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.resolveRef
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.commits
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.commit
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.compare
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.commitFiles
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.threadFiles
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.threadCommits
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.list
      </code>
    </td>
    
    <td>
      emulated
    </td>
    
    <td>
      emulated
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.get
      </code>
    </td>
    
    <td>
      emulated
    </td>
    
    <td>
      emulated
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.create
      </code>
    </td>
    
    <td>
      emulated
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.submit
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.approve
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.reply
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.resolveThread
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases.list
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases.get
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases.latest
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases.getByTag
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases.downloadAsset
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases.create
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        releases.update
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks.list
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks.create
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks.update
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks.delete
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks.rotateSecret
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks.deliveries
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        webhooks.redeliver
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search.threads
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search.repos
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search.commits
      </code>
    </td>
    
    <td>
      experimental
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search.code
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        securityAlerts
      </code>
    </td>
    
    <td>
      dependency, code scanning, secret
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        eventKinds
      </code>
    </td>
    
    <td>
      heuristic
    </td>
    
    <td>
      heuristic
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        authKinds
      </code>
    </td>
    
    <td>
      <code>
        token
      </code>
      
      , <code>
        anonymous
      </code>
    </td>
    
    <td>
      <code>
        token
      </code>
      
      , <code>
        anonymous
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        limits
      </code>
    </td>
    
    <td>
      body 1048576, comment 1000000, label 255
    </td>
    
    <td>
      body 1048576, comment 1000000, label 255
    </td>
  </tr>
</tbody>
</table>
</provider-capabilities>


## Sitemap

See the full [sitemap](https://forges.link/sitemap.md) for all pages.
