Configure GitHub Delivery Attribution
Connect GitHub so AIControls can ingest merged pull requests and link them back to the AI sessions that produced them. See Productivity for how the attribution model works.
Choose an authentication method
| GitHub App (recommended) | Personal access token | |
|---|---|---|
| Identity | Org-level — not tied to any one person | Tied to the developer who created it |
| Rate limit | 5,000 requests/hour per installation | 5,000 requests/hour, shared with that user's other API usage |
| Breaks if... | The App is uninstalled | The token owner leaves the org, or the token expires/is revoked |
| Webhook setup | Automatic — GitHub delivers events to every repo the App is installed on | Manual — you add a webhook on each repository yourself |
| Setup effort | One-time App creation, then reusable across repos | Faster for a single repo / quick test |
Use a GitHub App for anything beyond a quick trial. Use a PAT only to evaluate the feature against one or two repos before committing to App setup.
Set up authentication
- GitHub App (recommended)
- Personal access token
In your GitHub organization, go to Settings → Developer settings → GitHub Apps → New GitHub App.
Fill in the required fields:
- GitHub App name — any name, up to 34 characters (e.g.
AIC Delivery Attribution) - Homepage URL — your AIControls workspace URL
- Webhook → Active — checked
- Webhook URL —
https://YOUR_WORKSPACE_URL/webhooks/github - Webhook secret — generate a random string and save it; you'll enter the same value in AIControls in a later step
- GitHub App name — any name, up to 34 characters (e.g.
Under Repository permissions, grant:
- Pull requests — Read-only
- Metadata — Read-only (selected automatically)
No other permissions are required — AIControls only reads pull request and commit metadata, never file contents.
Under Subscribe to events, check Pull request.
Set Where can this GitHub App be installed? to Only on this account, then click Create GitHub App.
On the App's settings page, scroll to Private keys and click Generate a private key. A
.pemfile downloads — keep it safe, you'll paste its contents into AIControls and it can't be downloaded again (you can generate a new one if you lose it).Note the App ID shown near the top of the same page.
Click Install App in the left sidebar, choose your organization, select Only select repositories, and check every repository you want tracked.
After installing, the URL bar shows
.../settings/installations/<NUMBER>— that number is the Installation ID.
You should now have four values: App ID, Installation ID, the private key (.pem contents), and the webhook secret you set in step 2.
Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
Set Resource owner to your organization, and under Repository access, select the repositories you want tracked.
Under Permissions → Repository permissions, grant:
- Pull requests — Read-only
Metadata read access is included automatically for fine-grained tokens. Don't grant Contents — AIControls only calls pull-request endpoints and never reads file contents.
Generate the token and copy it — it's shown only once.
For each repository you selected, go to Settings → Webhooks → Add webhook and configure:
- Payload URL —
https://YOUR_WORKSPACE_URL/webhooks/github - Content type —
application/json - Secret — any random string; use the same value for every repo, you'll enter it once in AIControls
- Which events — select Pull requests only
- Payload URL —
You should now have two values: the PAT, and the webhook secret you set on each repo's webhook.
Configure the integration in AIControls
Go to Settings → Integrations, find the GitHub card, and open it.
Enter the Webhook secret — the same value you set on the GitHub App or on the repo webhooks above.
Fill in either of the following, not both:
- Access token (PAT) — if you're using the personal-access-token method
- App ID, Installation ID, and Private key (PEM) — if you're using the GitHub App method (paste the full contents of the downloaded
.pemfile, including the-----BEGIN/-----ENDlines)
Under Repositories to backfill, add each repository as
owner/name(e.g.nirmata/aicontrols), pressing Enter after each one. This list controls which repos are included in the 90-day historical backfill — webhook deliveries are ingested regardless of this list, as long as the App (or webhook) is installed on that repo.Click Save. AIControls validates the credentials, and a 90-day backfill of merged pull requests starts automatically in the background.
Verify it worked
Open the Productivity page (in the Dashboards section of the sidebar). Backfilled pull requests appear within a few minutes, depending on repo size and count.
Open or merge a test pull request in one of the tracked repos, and confirm it shows up on the page shortly after.
If nothing appears, check delivery health:
- GitHub App: on the App's settings page, go to Advanced → Recent Deliveries and confirm deliveries show a
200response - PAT / webhook: on the repo, go to Settings → Webhooks, click your webhook, and check Recent Deliveries
- GitHub App: on the App's settings page, go to Advanced → Recent Deliveries and confirm deliveries show a
Re-running backfill after an upgrade
Detection of self-declared markers (see Witnessed vs. self-declared) was added after Tier 1 shipped. If your workspace was already ingesting pull requests before marker detection was enabled, its historical PRs won't have markers recorded yet — the PR bodies and commit messages needed to detect them weren't stored at ingestion time.
You don't need a new integration or a fresh install to pick this up:
Go to Settings → Integrations, open the GitHub card, and click Backfill again (or use the Backfill button on the Productivity page itself). This re-fetches the trailing 90 days of merged pull requests and their commits through the same ingestion path as the original setup.
As PR bodies and commit messages are re-fetched, marker detection runs against them and existing rows are updated in place — no PRs are duplicated or re-counted.
Your historical AI-managed percentage may increase after this completes: PRs produced by tools that don't route through the proxy (a session on a direct API key, or a tool other than Claude Code) but that carry a self-declared marker will move from "manual" into "self-declared."
This only affects the trailing 90-day backfill window. PRs merged further back were never ingested and won't gain markers retroactively.
Group unmapped repos into a project
A project groups one or more repos so session cost and activity roll up together on the Activity → Projects tab. As soon as AIControls sees developer activity in a repo that isn't covered by any project yet, it appears as an unmapped repo — a N Repos Detected button shows above the Projects table.
Click N Repos Detected to open the Unmapped Repos drawer, and select one or more repos from the list.
Under Add to, either:
- Leave it on New project to group the selected repo(s) into a brand-new project (single repo: named automatically from the repo slug; multiple repos: you're prompted for a name), or
- Pick an existing project to merge the selected repo(s) into that project's repo list instead.
Click Create Project (or Add to Project, if an existing project is selected). The repo no longer shows as unmapped, and its historical sessions are re-attributed to the project.
Repos you don't want tracked as a project can be dismissed from the same drawer — dismissed repos stop being suggested as candidates.
Troubleshooting
"Configure either an access token or a GitHub App — not both"
The integration form rejects a config with both a PAT and App fields filled in, to avoid ambiguity about which credential is actually used. Clear whichever method you're not using.
Webhook deliveries show a signature failure
The Webhook secret in AIControls must exactly match the secret configured on the GitHub App (or on every repo's webhook, for the PAT method). Re-enter it on both sides if you're not sure they match.
Backfill shows 0 pull requests
Confirm the repositories are spelled correctly as owner/name under Repositories to backfill, and that the GitHub App (or PAT) actually has access to them — for a GitHub App, check Install App → Configure to see which repos are currently selected.
See also
- Productivity — how the attribution model works