---
title: "Tangled"
description: "Use Tangled, a forge built on atproto: issues, pulls, comments, live events and webhooks."
canonical_url: "https://forges.link/providers/tangled"
---
# Tangled

> Use Tangled, a forge built on atproto: issues, pulls, comments, live events and webhooks.

The Tangled provider works with [Tangled](https://tangled.org), a forge built on atproto. It can read and write issues, pulls, comments, labels, assignees, reactions and collaborators, merge pulls, read profiles, read repository contents from the knot that hosts each repository, stream live events from Jetstream and verify incoming push webhooks. Tangled has no reviews, so those capabilities are `false`.

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

<tbody>
  <tr>
    <td>
      Import
    </td>
    
    <td>
      <code>
        forges/tangled
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Factories
    </td>
    
    <td>
      <code>
        tangled()
      </code>
      
      , <code>
        tangledLite()
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Auth
    </td>
    
    <td>
      <code>
        anonymous
      </code>
      
      , <code>
        app_password
      </code>
      
      , <code>
        oauth
      </code>
    </td>
  </tr>
</tbody>
</table>
</provider-header>

<note>

The Tangled provider is experimental, because Tangled's lexicons are young and can still change. To learn what that means, see [Capabilities](https://forges.link/concepts/capabilities).

</note>

## Authentication

Reading is public and needs no credentials. Writing requires an atproto session, which you get from an app password or from an OAuth client:

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

const reader = tangled({}).create()

const writer = tangled({
  auth: {
    type: 'app_password',
    identifier: 'alice.example.com',
    password: process.env.TANGLED_APP_PASSWORD!,
  },
}).create()
```

With `app_password`, the provider creates a session on the PDS (personal data server) of the account. It looks for the PDS in this order:

1. The `pds` option, if you pass it.
2. The DID document, if `identifier` is a DID.
3. `https://bsky.social`.

With `oauth`, you pass the `did`, the `pds` and a `fetch` from an atproto OAuth client that signs requests:

```ts
tangled({
  auth: {
    type: 'oauth',
    did: session.did,
    pds: session.pds,
    fetch: session.fetch,
  },
})
```

## Instance and services

The `baseUrl` defaults to `https://tangled.org`. The provider uses it for web links and as the instance.

Tangled records are stored on many servers, so the provider has to read from several services. You can change each one of them with an option:

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Reads
    </th>
    
    <th>
      Default
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        plcUrl
      </code>
    </td>
    
    <td>
      DID documents, to find the PDS of each author
    </td>
    
    <td>
      <code>
        https://plc.directory
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        backlinksUrl
      </code>
    </td>
    
    <td>
      Records that point at a thread, such as comments
    </td>
    
    <td>
      <code>
        https://constellation.microcosm.blue
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        handleResolverUrl
      </code>
    </td>
    
    <td>
      The DID for a handle, when the <code>
        owner
      </code>
      
       of a ref is a handle
    </td>
    
    <td>
      <code>
        https://public.api.bsky.app
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        recordsUrl
      </code>
    </td>
    
    <td>
      Records, through a Slingshot-compatible cache
    </td>
    
    <td>
      The PDS of each author
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        jetstreamUrl
      </code>
    </td>
    
    <td>
      The live event stream
    </td>
    
    <td>
      A public Bluesky Jetstream instance
    </td>
  </tr>
  
  <tr>
    <td>
      None
    </td>
    
    <td>
      Repository contents, from the knot that the repo record names
    </td>
    
    <td>
      The repo's knot
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        apiUrl
      </code>
    </td>
    
    <td>
      <code>
        request()
      </code>
      
      , <code>
        listSource: 'index'
      </code>
      
      , and star and fork counts
    </td>
    
    <td>
      <code>
        https://api.tangled.org
      </code>
    </td>
  </tr>
</tbody>
</table>

## Refs and numbers

Issues and pulls are atproto records, and they are identified by their AT-URI. In a `ThreadRef`, the AT-URI is both the `number` and the `externalId`. The `owner` of a repo is the DID of its owner, and `RepoRef.externalId` is the DID of the repo.

In the refs that the provider returns, the `name` of a repo is the key of its repo record. For newer repos, that key differs from the name that people see, which `Repo.displayName` holds. You can also pass a ref that has a handle as `owner` and the visible name as `name`, which is what `parseUrl()` returns for a URL such as `https://tangled.org/@acme.example.com/widgets`. The provider resolves the handle and finds the record by its name, so the ref that it returns can differ from the one you passed.

`repos.byOwner()` reads the repo records of an account, which you name by its handle or DID. Every Tangled repo is public, and the records come back in their own order, so if you pass `sort`, you get a `sort_unsupported` warning.

`repos.get()` also reports `stars` and `forks`, from `sh.tangled.feed.countStars` and `sh.tangled.repo.countForks` on the index at `apiUrl`. The index can lag behind the appview, so these counts can be lower than the ones in the Tangled UI. Each count is absent when the index can't return it. Listings of repositories don't include them.

The numbers that you see in the Tangled UI and in webhooks come from the appview, because the records themselves don't store them. The provider reports them as `displayNumber`.

A ref from a webhook has no `number` until it is matched to a record. To fill in `number` during `webhooks.ingest()`, pass a `resolveDisplayNumber(repo, kind, displayNumber)` function that returns the AT-URI.

## Thread state

The state of a thread is the latest state record that was written by the author of the thread, the owner of the repo or a collaborator. The provider ignores state records from other accounts. If another account calls `close()` or `reopen()`, the call throws `InsufficientScopeError` and writes no record.

`close()` and `reopen()` read the thread after writing the state record, and resolve with it in the state they wrote, even while the backlink index still lists the earlier state.

## Labels and assignees

Labels are `sh.tangled.label.definition` records, and a thread's labels are the label ops that the owner of the repo or a collaborator wrote for it, applied in order. A label with a value, such as `priority`, appears as `priority:high`. Tangled stores assignees as an `assignee` label whose values are DIDs, so they appear in `assignees` rather than in `labels`.

Each call to `setLabels()`, `addLabels()`, `removeLabels()` or `setAssignees()` writes one label op with what it adds and removes. `update()` packs `labels` and `assignees` into a single op too, and writes it before the state record and the thread edit, so a name the repo doesn't have stops it before anything changes. Only the owner of the repo or a collaborator can write one that counts, so for any other account these calls throw `InsufficientScopeError`. A name must match a label that the repo record lists, or the call throws `NotFoundError`. `createLabel()` writes a definition that applies to issues and pulls and adds it to the repo record, so only the owner of the repo can call it.

The backlink index can take a while to list a new label op, so `setLabels()`, `addLabels()`, `removeLabels()` and `setAssignees()` don't read the labels back. They resolve with what the op leaves, worked out from the label ops they read before writing it.

`repos.collaborators()` lists the owner of the repo with the role `admin` and each collaborator with the role `write`. `permissionFor()` returns `read` for any other account, because every Tangled repo is public.

## Listing threads

By default, `threads.list()` reads every record and works out the state of each thread, which takes several requests per thread. If you set `listSource: 'index'`, it lists from the index at `apiUrl` instead. The index takes one request per page, but its states can lag behind the records, so every listing from the index has an `index_possibly_stale` warning. The index has no labels or assignees, so its threads have none, and it ignores the `labels` and `assignee` filters with a `filter_unsupported` warning.

## Repository contents

The `contents` verbs read files, trees, branches, tags and commits from the knot that the repo record names, so they need no credentials. Knots list one level of a tree at a time, so `tree()` with `recursive` returns one level and a `recursive_unsupported` warning. Knots don't filter commits, so `commits()` ignores `path`, `author`, `since` and `until`, and warns with `filter_unsupported`.

A pull carries its changes as a patch rather than as a branch. `threads.files()` and `threads.commits()` read the patch of the latest round. When several commits in the patch change the same file, that file's counts and patch are those of each commit together, and the listing has a `files_approximate` warning.

## Notifications

Notifications use the `org.tangled.temp.notification.*` lexicons. These lexicons are in Tangled's temporary namespace and can change. To enable notifications, pass `auth` and set `notificationsUrl` to the base URL of the notification service. The notification capabilities are `'experimental'` then.

Stars and follows have no thread. They appear with `kind: 'other'`, and `typeRaw` contains the type of the notification.

## Pulls and merging

A pull carries a patch rather than a branch. `threads.create()` with `kind: 'pull_request'` asks the knot for the `git format-patch` of the commits that `head` has and `base` lacks, uploads it to your PDS and writes the pull record. `head` must be a branch of the same repository; pulls from forks and drafts aren't supported.

`merge()` sends the patch of the latest round to the knot, which applies its commits to the target branch with `git am`, and then writes a `merged` status record. Because the commits are applied as they are, `rebase` is the only merge method, and `title` and `message` are ignored. Only the owner of the repository or a collaborator can merge. Tangled has no review step, so `approveAndMerge()` isn't available.

`repos.addCollaborator()` asks the knot to give an account push access. Tangled has a single collaborator role, so `role` must be `write`.

## Live events

`sources.subscribe()` streams events from Jetstream, and each item has a `cursor`. To resume a stream later, save the cursor and pass it back:

```ts
for await (const { event, cursor } of reader.sources.subscribe({
  cursor: saved,
  signal,
})) {
  console.log(event.kind, event.summary)
  saved = cursor
}
```

To close the connection, abort the signal or break out of the loop. If the connection drops, the iterator throws `SubscriptionClosedError` with the last cursor. The provider doesn't reconnect by itself, so call `subscribe()` again with that cursor.

## Activity feeds

An account's issues, pull requests, comments, state changes, reactions and label changes are records in its own repository, so `users.events()` reads them from its PDS. It takes a handle or a DID. Each page lists every collection the account has records in, and merges them by record key, which orders them by when they were written. The events are the ones `sources.subscribe()` yields for the same records, with `source: 'poll'`.

The first page's `etag` pairs the repository's revision with a digest of the records on that page. Pass it back as `cursor: { etag }`, with the same `perPage`, and you get a `notModified` page when nothing on it changed. An untouched revision takes a single request. A Bluesky post or a like moves the revision too, so the feed lists the records again, sees the same page and still answers `notModified` before it resolves a single thread. A chatty Bluesky account doesn't cost a full read on every poll. An edit to a record on the page does count as a change. The feed lists the records as they are now, so deleted records don't appear, and an edited record appears once.

Tangled has no feed of a repository's activity, because each contributor's records are in their own repository, and pushes go to the knot. `repos.events()` isn't available.

## Webhooks

To verify deliveries, set `webhookSecret` to the secret that you configured on the hook:

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

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

With each delivery, Tangled sends these headers:

<table>
<thead>
  <tr>
    <th>
      Header
    </th>
    
    <th>
      Content
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        X-Tangled-Signature-256
      </code>
    </td>
    
    <td>
      <code>
        sha256=
      </code>
      
       followed by the hex HMAC-SHA256 of the body
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        X-Tangled-Event
      </code>
    </td>
    
    <td>
      The event name
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        X-Tangled-Delivery
      </code>
    </td>
    
    <td>
      The delivery ID
    </td>
  </tr>
</tbody>
</table>

A `push` delivery becomes a `push` or `ref` event. Tangled has no API to register hooks, so the management methods of `webhooks` aren't available.

## Capabilities

<provider-capabilities provider="tangled">
<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>
      experimental
    </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>
      ❌
    </td>
    
    <td>
      ❌
    </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
    </td>
    
    <td>
      issue, PR
    </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
    </td>
    
    <td>
      issue, PR
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        comments.list
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      issue, PR
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        comments.get
      </code>
    </td>
    
    <td>
      issue (emulated), PR (emulated)
    </td>
    
    <td>
      issue (emulated), PR (emulated)
    </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>
      experimental
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.markRead
      </code>
    </td>
    
    <td>
      experimental
    </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>
      unverified
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.markAllDone
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        notifications.unreadCount
      </code>
    </td>
    
    <td>
      unverified
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        writes.comment
      </code>
    </td>
    
    <td>
      issue, PR
    </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>
      ❌
    </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>
      ❌
    </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>
      ❌
    </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>
      ❌
    </td>
    
    <td>
      ❌
    </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>
      ❌
    </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>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.get
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reviews.create
      </code>
    </td>
    
    <td>
      ❌
    </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>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search.code
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        securityAlerts
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        eventKinds
      </code>
    </td>
    
    <td>
      native
    </td>
    
    <td>
      native
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        authKinds
      </code>
    </td>
    
    <td>
      <code>
        anonymous
      </code>
      
      , <code>
        app_password
      </code>
      
      , <code>
        oauth
      </code>
    </td>
    
    <td>
      <code>
        anonymous
      </code>
      
      , <code>
        app_password
      </code>
      
      , <code>
        oauth
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        limits
      </code>
    </td>
    
    <td>
      unknown
    </td>
    
    <td>
      unknown
    </td>
  </tr>
</tbody>
</table>
</provider-capabilities>


## Sitemap

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