---
title: Authentication
description: Securely authenticate your workflows using personal access tokens (PATs).
---

Copilot uses two deliberately separate credentials:

- **Setup PAT (operator token):** entered in `copilot setup` or `copilot doctor`, used only for that local command, and never stored in the repository or in a GitHub Secret. The wizard validates it before making changes.
- **Workflow PAT (bot token):** owned by the bot account, stored as the repository or organization Secret `PAT`, and consumed by GitHub Actions at runtime. This is the token that gives the workflows their bot identity.

For [guarded PR approval](/pull-requests/guarded-approval), this same runtime PAT submits the native review. It must have Pull requests write permission and must resolve to a different immutable GitHub user ID from the PR author. The local setup PAT never approves; unreadable branch rules or an unverifiable runtime identity keep the feature in non-approval mode.

The setup PAT and workflow PAT may have different owners and permissions. Do not paste the workflow PAT into the setup prompt unless you intentionally want the same token to perform both roles.

## Permission tables in `copilot setup`

Immediately before each hidden PAT prompt, interactive setup prints a
least-privilege table with the GitHub permission, repository or organization
scope, access level, purpose, and any enabling condition. After a newly supplied
or re-entered token is entered, setup prints the same ordered matrix with one of
these states:

| Status | Meaning | Setup behavior |
|---|---|---|
| `✅ Verified` | A safe authentication-bound GitHub operation proved the requested read capability. | Continue. |
| `❌ Missing` | GitHub deterministically rejected a required capability after identity and repository access were established. | Stop before the dependent mutation and name the permission to grant. |
| `? Unverifiable` | GitHub does not expose a safe non-mutating proof of the PAT grant, or the response was ambiguous/transient. | Required reads block unless the exact successful public-repository read has separate operational evidence. Required writes pause for a separate explicit acknowledgement and remain non-verified. |

A `403` is not automatically a missing-permission result. Copilot reports it as
`Missing` only when bounded GitHub metadata explicitly identifies a permission
denial and there is no rate-limit, retry, or SSO signal. Bare responses, the
generic provider message `Forbidden`, rate-limited responses, SSO-constrained
responses, and otherwise ambiguous `403` responses remain `Unverifiable`; raw
provider messages are never printed. Malformed response bodies or unreadable
provider headers are handled the same way.

A successful `200` is not automatically permission evidence. Repository
metadata, commits, rulesets, labels, workflows, checks, pull requests, and
workflow files may be anonymously readable on a public repository. Copilot
marks those reads `Verified` only when the same bounded probe establishes that
the repository is private; on a public repository, or when visibility is
unknown, success remains `Unverifiable`. Secret and Variable inventory
reads are permission-bound; their corresponding write grants remain
`Unverifiable` because GitHub exposes no safe non-mutating proof of write
access, so setup requires explicit acknowledgement for those rows. Public
organization Issue Types reads remain `Unverifiable` as PAT evidence.
After valid token identity, a successful public repository read can be used
for that exact operation while its PAT permission row stays `Unverifiable`.
The public organization members list can omit concealed members, so it cannot
establish Members-read capability. Copilot instead checks the authenticated
user's active organization membership through GitHub's permission-bound
Members-read endpoint; only a valid response for the selected organization
marks that row `Verified`.
This positive operational fact is not granted to a failed, ambiguous, or
visibility-unknown probe, organization Issue Types, or any write.

The third state is intentional. GitHub's
`X-Accepted-GitHub-Permissions` response header describes what an endpoint
requires; it does not enumerate every effective grant of the presented
fine-grained PAT. Copilot never creates a temporary label, branch, file,
Variable, Secret, comment, project item, or workflow run merely to turn that
unknown into a checkmark.

`ready` is true only when every required row is read-level and each read is
verified or positively usable; the read-only audit never accepts a provider
claim that a write row is verified. A public read remains visibly
`Unverifiable` as token-permission evidence even when setup can use it. When
identity and all required reads are verified or usable and only required writes
remain `Unverifiable`,
interactive setup asks the operator to confirm that the PAT settings exactly
match the displayed table, defaulting to No. Unattended setup requires the
separate `--confirm-unverifiable-write-permissions` flag; `--yes` approves only
the setup plan and is not permission evidence. Missing access, unverifiable
required reads, and invalid identity can never be acknowledged past.

The setup-PAT table is comprehensive because it appears before the interactive
feature choices are final. Repository Metadata and the read capabilities needed
for initial inspection are required; later write and organization permissions
state the feature/storage condition that makes them applicable. The workflow-PAT
table is calculated from the final setup configuration, so guarded approval,
release/hotfix, organization issue types, Projects, and organization Variables
appear only when selected. That calculation uses the effective remote scope,
including a preserved organization-level `PR_APPROVAL_POLICY`; leaving an
existing organization Variable in place therefore still requires organization
Variables read access for the workflow PAT.

The repository Contents read check uses the read-only commit-list endpoint. A
documented empty-repository response is accepted as proof only after repository
metadata proves the repository is private. For a public repository that same
response remains `Unverifiable`; the corresponding write capability is also
unverifiable and needs the normal explicit acknowledgement. An ambiguous `404`
is never treated as empty-repository evidence.

Permission checks use at most four concurrent read-only GitHub probes. Results
remain in the displayed requirement order even when provider responses complete
out of order, reducing secondary-rate-limit pressure without making the report
nondeterministic.

If a conditional repository or organization Secret or Variable inventory read is unavailable
before feature selection, setup keeps that access state distinct from an empty
inventory and continues planning. When the approved plan actually needs that
resource at either scope, or must inspect both scopes to preserve an
unoverridden existing value, the final setup-PAT table promotes it to required and stops
before any dependent write until the named permission is corrected. If GitHub
can only report the permission as unverifiable and the required inventory
remains unavailable, setup still fails closed after the final table and before
credential choices or resource targeting; it never treats the missing inventory
as an empty list. Repository inventory is required even when every selected
resource is explicitly organization-scoped or uses an organization default
with `preserveExisting: false`: a same-name repository resource takes
precedence at runtime. If the repository inventory is unavailable, or it
reveals a same-name shadow, setup blocks before provisioning the organization
target. Choose repository scope or remove the shadow after inspecting it.
If required repository or organization inventory is unavailable, the wizard
recognizes that blocked result immediately after running the final setup-PAT
permission audit. The CLI reports the storage validation error with a failing
exit code without starting a second audit or inventory validator. It does not
continue into plan confirmation, credential collection, workflow comparison,
target resolution, or mutation.
The provisioning workflow repeats this fail-closed rule after approval: if it
cannot obtain an authoritative remote snapshot or required selected inventory
access, it stops before Secrets, Variables, labels, issue types, and tag writes
with bounded recovery guidance, including for a repository-scope default.

<Info>GitHub does not reveal Secret values through its API. Copilot validates new credentials with provider metadata requests and validates existing remote Secrets through the read-only `copilot_credential_health.yml` workflow. Setup proves the exact file on the selected main ref and dispatches that path only when the workflow is also registered or installed on GitHub's default branch; doctor remains query-only and expects the normally indexed installed workflow. The health workflow reports each requested credential independently, but that bounded reachability result is not a permission audit. If `PAT` already exists, interactive setup asks you to re-enter it and runs the complete workflow-PAT permission matrix before provisioning; unattended setup must supply `PAT` again or stops before mutation. Temporary workflow bootstrap is available only during setup. A preauthenticated Codex session is runner state, not a Secret: it is accepted only when the runtime preflight can execute `codex login status` successfully.</Info>

For other existing credentials, choosing `keep` works only when the selected
storage policy preserves the Secret in its current repository or organization
scope. With `preserveExisting: false`, or an override that moves the Secret,
setup requests and validates the value again before provisioning the new target.

An Actions API `404` does not by itself mark the credential-health workflow as
missing. Setup first proves repository Contents visibility, then reads the exact
workflow path. Only a subsequent exact-path `404` is confirmed absence; every
unreadable or ambiguous state remains `unavailable` and never authorizes
temporary workflow creation. The initial check may use the default branch;
after the questionnaire chooses the main branch, setup repeats both Contents
reads on that selected ref before its final PAT audit. A failed selected-ref
lookup cannot inherit the default-branch result. The setup-only bootstrap
adapter independently repeats the selected-ref reads before dispatch or any
write, even if Actions finds a workflow on the default branch. If the final PAT
audit denies required access, setup reports a bounded blocked result with the
selected configuration before storage validation or mutation.
An exact-file response counts as installed only when it identifies a file with
a non-empty `sha`; an empty object or directory-like response is unavailable.
Once installed state is proven on the selected ref, setup dispatches that path
and ref only if GitHub's Actions index resolves the workflow or, after an index
`404`, exact Contents inspection also proves it installed on the default
branch. A selected-ref-only file cannot be dispatched. Missing state follows
the bounded temporary-bootstrap flow only when a default-branch definition is
available, or when setup can install the temporary file on the default branch
itself. Temporary creation is create-only; setup records the created file SHA
and removes it only if the selected-ref file still has that same SHA. A
concurrent change is left intact with bounded cleanup guidance. Unavailable
state never dispatches or mutates.

<Warning>**When the event actor is the same as the token user**: The action detects this before entering the workflow queue. It completes successfully without waiting or running the normal issue/PR/push pipeline. A valid explicit single action still runs. This avoids the bot reacting to its own actions. Use a dedicated bot account (different from the actor) if you want full pipeline behavior on every event.</Warning>

For comment-driven assistance, read-only commands are available to anyone who can comment unless `ai-members-only` is enabled; with that policy, every requested agent task requires an authorized member, including generation of bounded dynamic product copy for a configured locale. Membership is checked only after live admission returns `execute`; no-op, blocked, and continuation-only outcomes do not query it. A locale-only catalog planner uses this membership check and does not require repository write permission. Non-AI status/help metadata remains available. File-modifying commands use a separate repository-write check: for both organization and personal repositories, the repository owner or a collaborator with `push`, `maintain`, or `admin` permission may request changes. Organization membership alone is not mutation authority. The workflow PAT still needs the relevant `contents: write` permission, and issue comments need an open PR to provide a branch for the change.

<Steps>
  <Step title="Who is the bot?">
    Choose which account will be used to create your PAT. This account will act as your bot.
    - For individual developers, it is recommended to use your own account as the bot.
    - In organizations and enterprise accounts, it is better to use a separate, dedicated account. For example, this project belongs to [**vypdev**](https://github.com/vypdev), and the selected bot account is [**vypbot**](https://github.com/vypbot).
  </Step>

  <Step title="Create the setup PAT">
    The person running setup needs a separate fine-grained PAT. Give it only the permissions required by the selected setup features: repository Metadata read and Contents read for inspecting repository files and installed workflows; Administration read when release/hotfix setup or doctor must inspect classic branch protection; Issues write for labels; Variables write for Repository Variables; Secrets read/write when provisioning Secrets; Actions read/write when checking or dispatching credential health; and organization Issue Types or Projects permissions only when those integrations are selected. There is no separate Workflows read permission for inspection. If setup will use organization-level Actions Secrets or Variables, the token also needs the corresponding organization Actions Secrets/Variables read and write permissions. Organization scope is valid only for repositories owned by an organization; setup detects personal repositories and stops before attempting organization writes. For existing Secrets, an installed `copilot_credential_health.yml` requires Actions write for dispatch but does not require Contents or Workflows write. Workflows write and Contents write appear only when the workflow is independently confirmed missing and temporary bootstrap is required; unavailable or unknown workflow state never authorizes temporary creation. Contents write can also be required for an initial tag or another explicitly selected repository mutation.

    Enter it in the hidden prompt, or use `--token`/`PERSONAL_ACCESS_TOKEN` for automation. It remains in memory for the command and is not written to `.env`, a config file, or the `PAT` Secret.

    Read the required-permissions table before creating the token, then review
    the permission-check table after entry. A `❌ Missing` required row must be
    corrected before setup can continue. An ambiguous required
    `? Unverifiable` read blocks; only a successful public-repository read can
    remain unverified as PAT evidence but operationally usable. A required
    write row means to compare the PAT settings with the
    requested access level and explicitly acknowledge it; it is never a pass.
  </Step>
  
  <Step title="Create the workflow PAT">
    Once you’ve selected the account that will act as the bot:
    - Go to [**Settings**](https://github.com/settings/profile).
    - Navigate to [**Developer settings**](https://github.com/settings/apps).
    - Then go to **Personal access tokens**.
    - In the left sidebar, select [**Fine-grained tokens**](https://github.com/settings/personal-access-tokens).
    - Click the [**Generate new token**](https://github.com/settings/personal-access-tokens/new) button.
    
    Now you can start configuring your new token. Set a meaningful name and description to easily distinguish it from other tokens. This might seem redundant, but it helps reduce the risk of accidentally deleting the token.

    Pay close attention to the **Resource owner** field:
    - If your bot account does not belong to an organization (individual developer), you should keep your own account selected when creating the token (this is usually the default value, so you likely won’t need to change it).
    - If your account belongs to an organization, make sure to select the organization that this token will operate under. 

    In the **Repository access** section, select only the repositories that will run Copilot. Avoid **All repositories** unless a separately reviewed organization policy genuinely requires it.

    The default installation enables issue, PR, comment, commit, release, and
    hotfix automation and therefore needs the write grants below. If you
    disable capabilities in setup, follow the final workflow-PAT terminal table
    instead: Metadata read is the only unconditional grant. For example, a
    repository using only PR review needs Pull requests write and Actions read
    for the previous-run queue check, not Actions write or Contents/Issues
    write; release/hotfix dispatch upgrades Actions to write, and
    file-editing routes or managed branches add Contents write. An issue
    workflow that remains selected while the issue route is disabled does not
    grant runtime authority.

    For the default installation, set these permissions for the repository:

    - **Actions**: Read-only for the previous-run queue check when any runtime route is enabled; read and write for release/hotfix dispatch
    - **Administration**: Read-only when release/hotfix orchestration or guarded PR approval is enabled, so runtime can inspect classic branch protection alongside effective rulesets.
    - **Checks**: Read-only when guarded PR approval is enabled, to verify current-head check runs and their producer App IDs. For ordinary pull-request automation, Checks is not required on the PAT: the supplied workflow grants job-local `checks: write` to its short-lived `GITHUB_TOKEN` for its own Check Run.
    - **Contents**: Read and write for managed issue branches, file-editing comment routes, or release/hotfix operations
    - **Issues**: Read and write for issue automation, issue comments, commit-driven issue progress, inactive issue closure, or release/hotfix lifecycle
    - **Metadata**: Read-only
    - **Pull requests**: Read and write for PR automation/comments, commit-driven Bugbot review, issue-comment autofix, guarded approval, or release/hotfix promotion
    - **Variables**: Read-only when guarded PR approval is enabled, to load `PR_APPROVAL_POLICY`. If setup creates, selects, or preserves an organization-level Variable, grant the corresponding organization Variables read permission as well.

    Do not grant Administration write, Secrets, Variables write, Webhooks, or Workflows permissions to the runtime PAT unless an independently reviewed extension actually uses them. Workflow installation and Secret/Variable administration belong to the separate setup PAT.

    **If your bot belongs to an organization** set these permissions for the organization:

    - **Issue Types**: Read and write only when issue-type automation is enabled
    - **Members**: Read-only only when a capability performs a membership lookup: automatic issue/PR assignees, automatic PR reviewers, release/hotfix issue authorization, or `ai-members-only` on an enabled issue, pull-request, commit, or comment route or on an independently available agent-backed single action. Enabling ordinary issue/PR comment automation alone does not require this grant. Setup also omits it when assignment/reviewer counts are zero and no other membership-consuming route is active.
    - **Projects**: Read and write only for the selected organization projects

    The runtime PAT does not need organization Secrets, Variables write, Custom repository roles, or Self-hosted runners administration. Organization Variables read is needed only when the approval policy is supplied at organization scope.

    Release and hotfix promotion/reconciliation PRs must still use this PAT (or
    an explicitly designed GitHub App token), rather than `GITHUB_TOKEN`, because
    most events produced by `GITHUB_TOKEN` do not start another workflow run.
    That is what allows the marked PR `closed` event to wake
    `copilot_deployment_orchestration.yml`.

    Finally press the **Generate new token** button.

    When setup requests this PAT, use its feature-derived terminal table as the
    authoritative checklist for the selected installation. Review every result
    row before allowing setup to store the token as the `PAT` Secret.

    <Warning>Make sure to **copy the generated PAT**, as it will not be visible again.</Warning>
  </Step>

  <Step title="Create the PAT Secret">
    It’s time to create the `PAT` Secret used by the workflows. The interactive setup can validate and create/update it directly when the setup PAT has Secrets write permission. Otherwise create it manually:

    If your bot account does **not** belong to an organization (individual developer):
    - Go to the repository where you want to implement Copilot.
    - Then, navigate to **Settings**.
    - In the left sidebar, click on **Secrets and variables**, then **Actions**.
    - Click **New repository secret**.
    - Define the name as `PAT` and paste the workflow PAT in the **Secret** field.
    - Finally, click **Add secret**.

    If your bot account **does** belong to an organization:
    - Go to the **Settings** of your organization.
    - In the left sidebar, click on **Secrets and variables**, then **Actions**.
    - Click **New organization secret**.
    - Define a name for the secret and paste the previously created PAT in the **Secret** field.
    - Select only the repositories that run Copilot. Avoid **All repositories** unless a separately reviewed organization policy requires it.
    - Finally, click **Add secret**.
  </Step>
  <Step title="Consume the token">
    In each workflow that consumes Copilot, make sure to pass the PAT you just created with the `token` property:

    ```yml
    name: Copilot - Issue

    on:
      issues:
        types: [opened, reopened, edited, labeled, unlabeled, assigned, unassigned]

    jobs:
      git-board-issues:
        name: Git Board - Issue
        runs-on: ubuntu-latest
        steps:
          - uses: vypdev/copilot@v3
            with:
              project-ids: 1,2
              token: ${{ secrets.PAT }}
    ```
  </Step>
</Steps>
