Skip to content

Connect GitHub

Connect a GitHub organisation with one read-only fine-grained personal access token so leancosts shows what the organisation is billed for Actions, Packages, Codespaces, Storage and Copilot next to your cloud spend, and who holds a Copilot seat nobody is using. leancosts is read-only here as everywhere: every call it makes to GitHub is a GET. For the full posture see Security and data handling.

  • You need to be an owner or admin of the GitHub organisation to create a token with organisation permissions, and the connections-manage permission in leancosts to add a connection.
  • Your organisation must allow fine-grained personal access tokens. Check it under the organisation’s Settings → Third-party Access → Personal access tokens. If tokens are not allowed there, the token you create will see nothing and the connection test will fail.
  • GitHub support is rolling out. If the GitHub card in the Add-connection picker is disabled and reads “Not enabled in this deployment”, there is nothing to set up yet; it opens when it is ready.

What leancosts reads, and what the token allows

Section titled “What leancosts reads, and what the token allows”
  • What we read. The organisation’s billing usage per day, product, SKU and repository, and the Copilot seat list: each assignee’s login, plan and last activity. Logins are personal data; check that this is acceptable under your privacy obligations (LGPD, GDPR or your own policy) before connecting.
  • What the token can do. A fine-grained token limited to this organisation with two read-only permissions. leancosts calls only GET endpoints: orgs/{org}, orgs/{org}/copilot/billing, orgs/{org}/copilot/billing/seats and organizations/{org}/settings/billing/usage. It never creates, edits or deletes anything.
  • How it is stored. AES-256-GCM encrypted, the same primitive Azure service-principal secrets use, and never logged or echoed back.

1. Create a fine-grained personal access token

Section titled “1. Create a fine-grained personal access token”

In GitHub, open Settings → Developer settings → Personal access tokens → Fine-grained tokens and click Generate new token.

  1. Resource owner: pick the organisation, not your personal account. This is the setting that decides what the token can ever see.
  2. Repository access: none needed. leancosts reads nothing per repository; the repository names it shows come from the bill.
  3. Organization permissions: set exactly two, both Read-only:
    • Administration: the billing usage.
    • GitHub Copilot Business: the seat roster.
  4. Expiration: tokens expire. Pick any date: leancosts reads the expiry back from GitHub on every call it makes and counts it down on the connection, so you do not have to keep your own reminder. The token still stops working on the day, and the sync stops with an authentication error, so rotate before then.
  5. Generate the token and copy it once (github_pat_...); GitHub will not show it again. If your organisation requires approval for fine-grained tokens, an organisation owner must approve the request before the token works.
  1. In leancosts go to Admin → Connections and click Add connection.
  2. Pick GitHub.
  3. Give the connection a nickname, enter the organisation login exactly as it appears in its URL (for example acme-org), pick a sync schedule (default every 6 hours), paste the token and click Create connection.
  4. The row shows Validating… while leancosts proves the token against the organisation, then Connected. Test re-runs that check any time and reports the organisation and the Copilot seat count.

The connection tells you when the token expires, under Open → Settings → Credential. GitHub reports its tokens’ expiry on every call, so after the first test or sync it shows GitHub’s own date, labelled from GitHub. Before then it shows the date you entered, labelled as entered, or Personal access token expiry not recorded with an Add date action (Change date once one is stored), recording a date never asks you for the token. Inside 30 days a badge appears on the connection row too, warning at 14 days and urgent at 3. The savings digest carries the same warning to everyone who can manage connections.

The first sync reads the organisation, the Copilot seat roster, and then the billing usage one month at a time, three months back by default. Sync on the row, and Sync now and Force refresh in the connection’s Settings, work like the cloud connectors; Pause stops the schedule without removing anything. Open on the connection row shows its sheet: Settings holds the name, the sync schedule, the credential and Remove; Console tails the sync log.

  • Costs gains GitHub spend as its own provider: the product (actions, copilot, packages, codespaces, …) is the service, the repository is the resource, and the money is what GitHub charged after your plan’s included allowances. A fully discounted line still shows, at zero, because it is the evidence your included minutes are being consumed.
  • Budgets, forecasts, anomaly alerts and email digests treat it like any other spend, because it is in the same cost model.
  • The Savings Register gains one GitHub finding per connection: Copilot seats GitHub itself recorded no activity on in the last 30 days. It is priced only from the seat price your own bill shows; when the bill does not pin a single seat price it stays a count with no dollar figure attached.
  • GitHub spend does not count toward your leancosts bill. You connected it for governance, not for metering.

You should see, within one sync:

  1. The connection row reads Connected and its meta line names the organisation, the numeric organisation id and the Copilot plan (or “no Copilot”).
  2. The connection’s Console tab shows three phases: organisation, Copilot seats, then one line per month of billing usage.
  3. Costs, filtered to GitHub, totals the same as GitHub’s own billing usage for the same months. Compare against the API directly:
Terminal window
ORG=acme-org
curl -sS -H "Authorization: Bearer $GITHUB_PAT" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/organizations/$ORG/settings/billing/usage?year=2026&month=9" \
| jq '[.usageItems[].netAmount] | add'

If the organisation has no Copilot, the connection still verifies and the bill is still ingested; the seat roster is simply empty and the seats finding is withheld rather than claimed.

Open → Settings → Remove purges the Copilot seat roster and every GitHub cost row the connection ingested. The data cannot be refreshed once the connection is gone, so removal always purges. Delete the token in GitHub afterwards.