Personal Access Token

A personal access token is a credential tied to one user account that authenticates API and Git requests as that person, under that person's permissions.

A personal access token is a credential tied to one user account that authenticates API and Git requests as that person. It is presented in place of a password, and every request it makes runs under that account's permissions, narrowed by the scopes or the permission set chosen when the token was created.

The consequences follow from that single sentence. The token's reach is a property of a human account rather than of the project it automates, so the questions worth answering are how wide that reach is, how long the credential lives, and what happens to the automation when the person behind it changes roles.

Definition

Three properties describe the credential.

  • Bound to a person. The token is created under one user account and acts as that account. Audit entries, commits, comments, and API writes are attributed to that person.
  • Presented as a bearer credential. It travels in an Authorization: Bearer header for REST and GraphQL calls, and as the password in a Git operation over HTTPS.
  • Bounded at creation. The creator picks the scopes or permissions, an optional or required expiration date, and, for the fine-grained type, the repositories the token may reach.

The two token types

GitHub issues two kinds of personal access token. They differ in how reach is expressed and in how much control an organization has over them (GitHub documentation on managing personal access tokens and the organization token policy documentation, both checked on 2026-08-13).

PropertyClassic tokenFine-grained token
Prefixghp_github_pat_
Repository reachevery repository the owner can access, present and futureone resource owner, then all or a named list of its repositories
Permission modelcoarse scopes such as repo, workflow, admin:org, write:packagesper permission read or write, such as Contents, Issues, Pull requests, Actions
Metadata permissionimplied by the scopesrepository Metadata read is mandatory
Expirationset at creation, including a no expiration optionset at creation, with a custom value of up to 366 days
Organization controlan owner can block classic tokens from reaching organization resourcesan owner can require approval before the token reaches organization resources
SAML single sign-onauthorization per organization requiredauthorization per organization required

Expiration is also governed from above. An organization or enterprise owner can set a maximum token lifetime policy, and a token created against that owner's resources gets the shorter of the requested value and the policy value.

The repo scope on a classic token is the one that surprises people. It grants read and write on every private repository the account can already open, so a token created to update one manifest file can also push to anything else that account touches. The workflow scope is a second checkpoint: without it, a push that modifies a file under .github/workflows is rejected.

Lifetime and revocation

A token stops working in five ways: the expiration date passes, the owner deletes it, an organization owner revokes its access to that organization, the underlying account is suspended or removed, or GitHub revokes it after finding it exposed. GitHub scans public repositories for its own credential formats and acts on matches, and push protection can block a commit that contains one before it lands (GitHub secret scanning documentation, checked on 2026-08-13).

The fourth case is the one that breaks scheduled automation. An offboarding removes the account, and the nightly job that used the token fails on a night nobody was expecting a deploy problem.

Rate limits are also personal. Requests authenticated with a personal access token count against a budget of 5,000 requests per hour for that user, shared by every script, tool, and workflow using any token that account owns (GitHub rate limits for the REST API, checked on 2026-08-13).

Example

A workflow reaches for a personal access token when the automatic per job credential cannot do the work. GITHUB_TOKEN is scoped to the repository that contains the workflow, and events it raises start no new workflow runs, so a job that has to write to a second repository or to trigger a downstream workflow needs a different credential.

The token is stored as a repository secret and read in the workflow like any other secret:

name: sync-config
on:
  schedule:
    - cron: "0 3 * * *"

jobs:
  propagate:
    runs-on: warp-ubuntu-latest-x64-2x
    steps:
      - uses: actions/checkout@v4
        with:
          repository: acme/platform-config
          token: ${{ secrets.CONFIG_SYNC_PAT }}
          path: platform-config

      - run: ./scripts/render-config.sh platform-config

      - name: Open a pull request
        env:
          GH_TOKEN: ${{ secrets.CONFIG_SYNC_PAT }}
        working-directory: platform-config
        run: |
          git switch -c sync/${{ github.run_id }}
          git commit -am "sync rendered config"
          git push origin HEAD
          gh pr create --fill --base main

Two details decide whether this workflow is safe. The secret named CONFIG_SYNC_PAT is encrypted at rest and injected into the job only when a step references it, and GitHub masks matching strings in the log output. Masking covers accidental echoes rather than deliberate exfiltration, because any step in the job, including a third-party action pinned to a moving tag, runs with the same environment. The guide to managing secrets in GitHub Actions covers the storage and review side of that.

The second detail is the token's reach. A fine-grained token limited to acme/platform-config with Contents write and Pull requests write does exactly the work above. A classic token with the repo scope, created by an engineer who can open forty repositories, does the work above plus anything else in those forty.

Why an application identity usually fits automation better

The same job written against an application identity changes what the secret store holds. An App ID and a private key are exchanged at the start of the run for an installation token limited to one repository:

      - uses: actions/create-github-app-token@v2
        id: app-token
        with:
          app-id: ${{ vars.CONFIG_SYNC_APP_ID }}
          private-key: ${{ secrets.CONFIG_SYNC_APP_KEY }}
          owner: acme
          repositories: platform-config

      - uses: actions/checkout@v4
        with:
          repository: acme/platform-config
          token: ${{ steps.app-token.outputs.token }}
          path: platform-config

Four things change with it. The credential in the job lives one hour, rather than for whatever lifetime a stored token was given. The pull request is attributed to the app's bot account, so a reviewer can tell automated changes from human ones at a glance. The permission set was declared by the app and approved by whoever installed it, which makes it reviewable outside the workflow file. And the automation keeps running when the engineer who built it leaves. The GitHub App page covers the registration, installation, and token exchange behind that snippet.

Personal access tokens still fit the cases an app cannot cover: a script on a laptop, a personal automation, a one-off migration, or a tool with no app registration. For those, prefer the fine-grained type, set the shortest expiration the task tolerates, list one repository where possible, and record a rotation date somewhere a person will read it.

FAQ

What is a personal access token?

A personal access token is a credential created by an individual user that authenticates API and Git requests as that user. It is presented as a bearer credential in place of a password, and every request it makes runs under the permissions of the account that created it, narrowed by the scopes or permissions chosen at creation.

What is the difference between a classic and a fine-grained personal access token?

A classic token carries broad scopes such as repo and workflow, and those scopes apply to every repository the owner can reach. A fine-grained token names one resource owner, lists the repositories it may touch, and sets each permission to read or write individually. A fine-grained token also takes an expiration set at creation, with a custom value of up to 366 days, and an organization owner can cap that with a maximum token lifetime policy.

Should a GitHub Actions workflow use a personal access token?

Use one when a job needs to reach a repository outside the one running the workflow, or needs a push to start another workflow run, and no application identity is available. An application identity usually fits better because its credentials are minted per run and expire in an hour, its permissions are approved at install time, and it survives the offboarding of the person who set the automation up.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.