---
title: "Tangled"
description: "Use Tangled, a forge built on atproto: issues, pulls, comments, live events and webhooks."
canonical_url: "https://forgeslink-3mwmle35v-poetry.vercel.app/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, releases or security alerts, so those capabilities are `'not_applicable'`.

<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>
    </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://forgeslink-3mwmle35v-poetry.vercel.app/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({})

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

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.

`parseUrl()` reads repository pages and the pages for threads (`/issues/{number}` and `/pulls/{number}`), commits (`/commit/{sha}`), files (`/blob/{ref}/{path}`) and comparisons (`/compare/{base}...{head}` or `/compare/{base}..{head}`).

Tangled names a repository by the handle of its owner, so the provider reads the handle from the owner's DID document and reports it as `RepoRef.ownerHandle` and as the `login` of `Repo.owner`. The `owner` stays the DID, so that a repository is found the same way whichever is known. An owner whose DID document claims no handle is named by its DID, which Tangled redirects to the handle.

`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.

## Thread numbers

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`.

The appview numbers a repository's issues and pulls separately. The provider reads the number from the appview's list of the repository's threads, at `/{owner}/{repo}/issues` and `/{owner}/{repo}/pulls`, and finds a thread in it by the account that opened it and by its title, and, where several threads share those, by the moment the appview saw them. A thread that the list does not name, or names ambiguously, has no `displayNumber` and no `url`.

The list costs a request per hundred threads, plus one to reach its end, and is cached for the repository. To read one thread, the provider reads the list of the threads that its author opened first, and the whole list only when that one does not name it.

Pass a `displayNumber` in the ref you give to `threads.get()` to use the number you know, such as the one a webhook carried.

The web page of a thread carries that number, so `Thread.url` and the URLs of comments point at the page that the appview serves.

In text, a thread is mentioned as `#<number>`, which is the display number: the record's AT-URI is not known from the text alone, so `parseReferences()` returns a ref that carries `displayNumber`. `urlFor()` links that ref, and `referenceFor()` writes it back as `#<number>`, or `owner/name#<number>` for another repository, with the owner's handle where the ref carries one.

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.

`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.stream()` 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.stream({
  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 `StreamClosedError` with the last cursor. The provider doesn't reconnect by itself, so call `stream()` 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.stream()` 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,
})

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.stream
      </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>
        users.block
      </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.issueTypes
      </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.branchRules
      </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>
        orgs.block
      </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.closingIssues
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.closingPullRequests
      </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>
        threads.comments
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      issue, PR
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.getComment
      </code>
    </td>
    
    <td>
      issue (emulated), PR (emulated)
    </td>
    
    <td>
      issue (emulated), PR (unverified)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.createComment
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.updateComment
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.deleteComment
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.hideComment
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.reactions
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      issue, PR
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.react
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.create
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.update
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.close
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.reopen
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.lock
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.setLabels
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.addLabels
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.removeLabels
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.setMilestone
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.setIssueType
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.setAssignees
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.requestReview
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.merge
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.updateBranch
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.transfer
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.markDuplicate
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.subscription
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.subscribe
      </code>
    </td>
    
    <td>
      issue, PR
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.checks
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.files
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.commits
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.reviews
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.getReview
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.createReview
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.submitReview
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.approve
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.replyToReviewComment
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.reviewThreads
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threads.resolveReviewThread
      </code>
    </td>
    
    <td>
      ∅
    </td>
    
    <td>
      ∅
    </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>
        installations.list
      </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.getRun
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.jobs
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.getJob
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.log
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ci.rerunFailed
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.getFile
      </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.compare
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contents.commitFiles
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.list
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.get
      </code>
    </td>
    
    <td>
      ✅
    </td>
    
    <td>
      ✅
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.comments
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.getComment
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.createComment
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.updateComment
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.deleteComment
      </code>
    </td>
    
    <td>
      ❌
    </td>
    
    <td>
      ❌
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        commits.hideComment
      </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.ingest
      </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.list
      </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://forgeslink-3mwmle35v-poetry.vercel.app/sitemap.md) for all pages.
