NETWORG internal — July 2025
How to handle GitHub authentication, Copilot licensing, and identity split for ephemeral AI coding containers.
Core finding: GitHub Copilot CLI supports credential isolation via COPILOT_GITHUB_TOKEN — enabling a split where Copilot authenticates as one identity and git operates as another. However, GitHub's Terms of Service prohibit sharing a single Copilot seat across multiple users, making the "shared account" approach non-viable as designed.
Context: NETWORG uses GitHub Team plan (not Enterprise) with
github-organization-managementfor custom Entra ID → GitHub sync — specifically to avoid the cost of GitHub Enterprise. EMU/SCIM are not available. Copilot Business ($19/user/month) IS available on the Team plan.
Key recommendations:
-
Licensed developers → Standard OAuth flow via the AgentBox GitHub App. User's own
ghu_*token powers both Copilot CLI and git. This is already designed inidentity.md. -
Unlicensed developers → Purchase a Copilot Business seat for each developer who will use AgentBox, even if they're "light-use". At $19/user/month, this is the only ToS-compliant approach. Alternatively, designate unlicensed users as Copilot-free and provide them with an AgentBox that has all other tools but no Copilot CLI.
-
Copilot license detection → Use
GET /orgs/{org}/members/{username}/copilotto check entitlement at spawn time. The existinggithub-organization-managementapp already has the GitHub App infrastructure and Entra ID ↔ GitHub identity mapping needed. -
Identity mapping → Already solved. The
github-organization-managementrepo stores GitHub user IDs in Entra ID directory extension attributes. Extend this to drive AgentBox's spawn-time license checks.
| Endpoint | Method | Purpose |
|---|---|---|
/orgs/{org}/copilot/billing |
GET | Org-level Copilot subscription summary + feature policies |
/orgs/{org}/copilot/billing/seats |
GET | List all assigned Copilot seats (paginated) |
/orgs/{org}/members/{username}/copilot |
GET | Check if specific user has a Copilot seat ⭐ |
/orgs/{org}/copilot/billing/selected_users |
POST | Add Copilot seats for specific users |
/orgs/{org}/copilot/billing/selected_users |
DELETE | Remove Copilot seats |
/orgs/{org}/copilot/billing/selected_teams |
POST | Add Copilot seats for entire teams |
/orgs/{org}/copilot/billing/selected_teams |
DELETE | Remove team Copilot access |
Note: All endpoints are in public preview and subject to change.
This is the key endpoint for AgentBox's spawn flow:
curl -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <TOKEN>" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/orgs/NETWORG/members/octocat/copilotResponse (200 — user has seat):
{
"assignee": {
"login": "octocat",
"id": 1,
"type": "User"
},
"assigning_team": {
"name": "Justice League",
"slug": "justice-league"
},
"pending_cancellation_date": null,
"last_activity_at": "2024-01-15T10:30:00Z",
"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
"last_authenticated_at": "2024-01-15T10:30:00Z",
"created_at": "2024-01-01T00:00:00Z",
"plan_type": "business"
}Response (404 — user does NOT have seat):
{
"message": "Not Found"
}Response (422 — Copilot not enabled for org):
{
"message": "Copilot Business or Enterprise is not enabled for this organization"
}curl -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <TOKEN>" \
https://api.github.com/orgs/NETWORG/copilot/billingResponse:
{
"seat_breakdown": {
"total": 50,
"added_this_cycle": 10,
"pending_cancellation": 2,
"pending_invitation": 3,
"active_this_cycle": 45,
"inactive_this_cycle": 5
},
"public_code_suggestions": "allow",
"ide_chat": "enabled",
"platform_chat": "enabled",
"cli": "enabled",
"seat_management_setting": "assign_selected",
"plan_type": "business"
}| Auth Method | Required Scope | Access Level |
|---|---|---|
| Classic PAT | manage_billing:copilot or read:org |
Read-only endpoints |
| Classic PAT | manage_billing:copilot or admin:org |
Write endpoints (add/remove seats) |
| GitHub App | organization_copilot_seat_management: read |
Read-only |
| GitHub App | organization_copilot_seat_management: write |
Full management |
Important: Only organization owners can query these endpoints. The token must belong to (or act on behalf of) an org owner.
✅ GitHub Apps can access Copilot billing endpoints with the organization_copilot_seat_management permission. This is an organization-level permission set during app registration.
For AgentBox, the recommended approach is to add this permission to the existing github-organization-management GitHub App (which already has org-level access), or to the AgentBox GitHub App if it has org owner authorization.
The API also supports adding/removing seats programmatically:
# Add a seat for a user on-demand
curl -L -X POST \
-H "Authorization: Bearer <TOKEN>" \
https://api.github.com/orgs/NETWORG/copilot/billing/selected_users \
-d '{ "selected_usernames": ["newuser"] }'
# Response: { "seats_created": 1 }
# Remove a seat
curl -L -X DELETE \
-H "Authorization: Bearer <TOKEN>" \
https://api.github.com/orgs/NETWORG/copilot/billing/selected_users \
-d '{ "selected_usernames": ["olduser"] }'
# Response: { "seats_cancelled": 1 }This opens up a possible just-in-time seat provisioning model (see Section 6).
The NETWORG/github-organization-management repository already implements Entra ID ↔ GitHub identity mapping. Key findings:
Architecture: ASP.NET Core 8.0 web app using:
Microsoft.Identity.Webfor Entra ID authenticationMicrosoft.Graph(v5.93) for Azure AD user managementOctokit(v14) for GitHub API
Identity mapping mechanism:
- GitHub numeric user ID stored as
long?in an Entra ID directory schema extension attribute on each User object - Extension attribute name configured via
AzureAd:ExtensionAttributeName(e.g.,extension_<appid>_githubId) - Mapping persisted in Entra ID — no separate database needed
- Data model:
GraphUserDto { Id: string (Entra object ID), GitHubId: long? (nullable GitHub user ID), Upn: string } - The
GraphUserDto.From(User)factory readsUser.AdditionalData[ExtensionAttributeName]and parses tolong
Account linking flow:
- User signs in with Entra ID (OpenID Connect)
- User clicks "Link GitHub Account" → redirected to GitHub OAuth (
read:user,user:emailscopes) - App extracts GitHub numeric user ID from OAuth response
- GitHub ID written to Entra ID user extension attribute via Microsoft Graph PATCH (
User.ReadWrite.Allapplication permission) - Mapping is immediately available to all systems reading from Entra ID
Sync process (/api/sync):
- Gets all GitHub App installations across orgs
- For each org: reads team descriptions (format:
Entra: <group-id>) to map teams → Entra ID groups - Resolves GitHub IDs from Entra ID extension attributes
- Invites missing users, removes departed users, syncs team memberships
- Idempotent, stateless, containerized
| Approach | Status | Pros | Cons |
|---|---|---|---|
| Entra ID extension attribute (current) | ✅ Already implemented | No extra database, single source of truth, works with Graph API | Requires user to self-link once |
| GitHub EMU (Enterprise Managed Users) | ❌ Not applicable | Full SSO, SCIM provisioning | Requires GitHub Enterprise Cloud ($$$). NETWORG uses GitHub Team plan specifically to avoid Enterprise costs. EMU accounts also can't interact with public repos. |
| SCIM provisioning | ❌ Not available | Automatic user lifecycle | Only available with GitHub Enterprise Cloud EMU, which NETWORG doesn't have |
| Manual mapping table in API | ❌ Redundant | Simple | Duplicates what Entra extension already does |
| GitHub API email lookup | No user action needed | Email must be public on GitHub; many users hide it |
Do not build a new identity mapping system. The github-organization-management app already:
- ✅ Links Entra ID users to GitHub accounts
- ✅ Stores the mapping in Entra ID (single source of truth)
- ✅ Has a GitHub App with org-level access
- ✅ Has Microsoft Graph integration for reading user attributes
AgentBox should:
- Read the same Entra ID extension attribute at spawn time to resolve GitHub username
- Use the resolved GitHub username to check Copilot entitlement via the API
- Require users to link their GitHub account (via the existing linking UI) before spawning an AgentBox
GitHub Copilot CLI uses a priority-based token lookup that is completely independent from git credentials:
| Priority | Source | Used For |
|---|---|---|
| 1 (highest) | COPILOT_GITHUB_TOKEN env var |
Copilot CLI only |
| 2 | GH_TOKEN env var |
Copilot CLI + gh CLI |
| 3 | GITHUB_TOKEN env var |
Copilot CLI + gh CLI |
| 4 | OAuth token from keychain (copilot login) |
Copilot CLI only |
| 5 (lowest) | gh auth token fallback |
Copilot CLI via gh |
Git credentials are entirely separate:
- Git uses
credential.helper,GIT_ASKPASS, or inline URL tokens GIT_AUTHOR_NAME,GIT_AUTHOR_EMAIL,GIT_COMMITTER_NAME,GIT_COMMITTER_EMAILcontrol commit identitygit pushauthentication uses whatever credential helper or token is configured
This means you CAN do:
# Copilot CLI authenticates as shared-account (for license)
export COPILOT_GITHUB_TOKEN=ghu_shared_account_token
# Git authenticates as the real developer
export GIT_AUTHOR_NAME="Real Developer"
export GIT_AUTHOR_EMAIL="real@networg.com"
export GIT_COMMITTER_NAME="Real Developer"
export GIT_COMMITTER_EMAIL="real@networg.com"
# Git push uses a different credential
git config credential.helper '!f() { echo "username=x-access-token"; echo "password=ghu_real_user_token"; }; f'| Token Type | Prefix | Works with Copilot CLI? | Notes |
|---|---|---|---|
| User access token (OAuth) | ghu_* |
✅ Yes | Preferred method |
| OAuth device flow token | gho_* |
✅ Yes | Via copilot login |
| Fine-grained PAT | github_pat_* |
✅ Yes | Must have "Copilot" permission |
| Classic PAT | ghp_* |
❌ No | Silently ignored |
| Installation token | ghs_* |
❌ No | Cannot use Copilot |
For unlicensed users, the split identity approach requires:
- Copilot CLI → authenticated as a shared/licensed GitHub account
- Git operations → authenticated as the real developer
Scenario A: Real user HAS a GitHub account but NO Copilot license
COPILOT_GITHUB_TOKEN = shared_account_ghu_token (Copilot)
GH_TOKEN = real_user_ghu_token (git push, gh CLI)
GIT_AUTHOR_EMAIL = real@networg.com (commit identity)
✅ Technically works. Git push uses real user's token. Copilot uses shared account.
Scenario B: Real user has NO GitHub account at all
COPILOT_GITHUB_TOKEN = shared_account_ghu_token (Copilot)
# No git push token available — who pushes?
# Option 1: GitHub App installation token (ghs_*) — acts as the app
# Option 2: Shared account pushes — but commits attributed to shared account
--author but push identity is the shared account or app.
GitHub Copilot is licensed per-user, non-transferable.
From GitHub's Copilot Product Specific Terms and general Terms of Service:
- Each Copilot license is tied to a specific GitHub user account
- "Account" means a personal account: one person = one account
- Sharing account credentials with multiple people violates GitHub ToS Section A.1
- Using a shared/bot account to proxy Copilot access to multiple developers = seat sharing
Risks of the shared account approach:
- Account suspension — GitHub may disable the shared account for ToS violation
- License revocation — Copilot entitlement can be revoked
- Audit trail corruption — Copilot usage telemetry attributed to wrong user
- No accountability — if AI generates problematic code, whose Copilot session was it?
Verdict: The shared Copilot account approach is a ToS violation regardless of how cleanly we split git identity.
Even if we used a shared account for everything, git allows overriding commit authorship:
git commit --author="Real Developer <real@networg.com>" -m "feat: add feature"
# or via environment variables:
export GIT_AUTHOR_NAME="Real Developer"
export GIT_AUTHOR_EMAIL="real@networg.com"Limitations:
- Author ≠ Committer:
git logshows both. The committer would still be the shared account - GPG signing: can't sign as the real developer if using shared account's token
- GitHub attribution: PRs pushed by shared account show shared account as contributor
- No verified badge: commits won't show as "verified" for the real developer
For scenarios where different repos need different credentials, git supports per-URL credential routing:
# In container's .gitconfig
[credential "https://github.com"]
helper = !f() { echo "username=x-access-token"; echo "password=${REAL_USER_TOKEN}"; }; f
# Copilot uses a completely separate env var
# COPILOT_GITHUB_TOKEN=<shared_token>This works because:
- Git credential helpers only affect
git clone/push/fetch - Copilot CLI reads
COPILOT_GITHUB_TOKENindependently - The two credential stores never interact
| Token | How Obtained | Lifespan | Copilot? | Git Push? | Billing API? |
|---|---|---|---|---|---|
User access token (ghu_*) |
OAuth web/device flow | 8hr (refreshable) | ✅ | ✅ | Only if user is org owner |
Installation token (ghs_*) |
JWT exchange by app | 1 hour | ❌ | ✅ | ❌ |
Fine-grained PAT (github_pat_*) |
Manual creation | Configurable | ✅ (with scope) | ✅ | ✅ (with scope) |
Classic PAT (ghp_*) |
Manual creation | Long-lived | ❌ | ✅ | ✅ |
The current design in identity.md is correct:
User signs in → Entra ID
↓
AgentBox API checks Entra ID extension attribute → gets GitHub username
↓
API checks: GET /orgs/NETWORG/members/{username}/copilot → 200 OK
↓
Standard GitHub App OAuth flow → user authorizes → ghu_* token
↓
Container spawns with:
GH_TOKEN=ghu_user_token ← powers Copilot CLI + git + gh CLI
(user.name/user.email from Entra ID profile)
| Option | Feasibility | ToS Compliant? | UX |
|---|---|---|---|
| A. Buy each user a Copilot seat | ✅ Simple | ✅ Yes | User gets full Copilot |
| B. Just-in-time seat provisioning | ✅ Possible | ✅ Yes | Auto-assign on spawn, auto-revoke on destroy |
| C. Shared Copilot account | ✅ Technical | 🚫 No | Split credentials work, but violates ToS |
| D. No Copilot for unlicensed users | ✅ Simple | ✅ Yes | Users get all tools except Copilot CLI |
| E. Copilot Free tier | ✅ Yes | 2,000 completions/month, limited features |
Option B (Just-in-Time Provisioning) is particularly interesting:
Spawn request → check Copilot entitlement
↓ (no seat)
API calls: POST /orgs/NETWORG/copilot/billing/selected_users
{ "selected_usernames": ["the-user"] }
↓
Seat assigned → user can now use Copilot
↓
Container destroyed → optionally remove seat:
DELETE /orgs/NETWORG/copilot/billing/selected_users
{ "selected_usernames": ["the-user"] }
Caveat: Copilot billing is per-seat per-month. Removing a seat mid-cycle still bills for the full month. So JIT only saves money if the user won't need Copilot again that billing cycle.
For users who don't need Copilot but do need git access:
AgentBox GitHub App → JWT signed with private key
↓
POST /app/installations/{id}/access_tokens
{ "repositories": ["target-repo"], "permissions": { "contents": "write", "pull_requests": "write" } }
↓
Returns ghs_* token (1-hour expiry)
↓
Container uses ghs_* for git clone/push
(commits show as "AgentBox[bot]" unless GIT_AUTHOR overridden)
User requests AgentBox spawn
│
├── Entra ID authentication (existing)
│
├── Read GitHub username from Entra ID extension attribute
│ └── Not linked? → Redirect to github-organization-management linking UI
│
├── Check Copilot entitlement:
│ GET /orgs/NETWORG/members/{username}/copilot
│
├── 200 OK (has seat) ─────────────────────────────┐
│ │ │
│ ├── GitHub App OAuth flow → ghu_* token │
│ │ │
│ └── Spawn container: │
│ GH_TOKEN=ghu_* (all operations) │
│ git user.name=<from Entra> │
│ git user.email=<from Entra> │
│ → Full Copilot CLI access ✅ │
│ │
├── 404 (no seat) ─────────────────────────────────┐
│ │ │
│ ├── Option A: Show "No Copilot license" warning │
│ │ └── Spawn without Copilot CLI │
│ │ (all other tools work: gh, az, etc.) │
│ │ │
│ ├── Option B: JIT seat provisioning │
│ │ └── POST to assign seat → then OAuth flow │
│ │ → spawn with full Copilot │
│ │ │
│ └── Option C: Copilot Free tier │
│ └── User's GitHub Free plan includes │
│ limited Copilot — may be sufficient │
│ │
└── 422 (no org Copilot subscription) ──────────────┘
└── Copilot not available for this org
Licensed user container:
# entrypoint.sh additions
export GH_TOKEN="${USER_GH_TOKEN}" # ghu_* from OAuth
export GITHUB_TOKEN="${USER_GH_TOKEN}"
export COPILOT_GITHUB_TOKEN="${USER_GH_TOKEN}"
# Git identity from Entra ID profile
git config --global user.name "${ENTRA_DISPLAY_NAME}"
git config --global user.email "${ENTRA_EMAIL}"
# Login to gh CLI
echo "${USER_GH_TOKEN}" | gh auth login --with-tokenUnlicensed user container (no Copilot):
# Only git operations via GitHub App installation token or user OAuth
export GH_TOKEN="${USER_GH_TOKEN}" # or ghs_* installation token
# Git identity from Entra ID profile
git config --global user.name "${ENTRA_DISPLAY_NAME}"
git config --global user.email "${ENTRA_EMAIL}"
# Copilot CLI disabled — no COPILOT_GITHUB_TOKEN set
# Optional: alias copilot to a message explaining why it's unavailable
alias copilot='echo "Copilot CLI requires a Copilot Business license. Contact your admin."'// In the AgentBox API (ASP.NET Core)
public async Task<SpawnResult> SpawnAgentBox(SpawnRequest request)
{
// 1. Get GitHub user ID from Entra ID extension attribute
// (same extension attribute used by github-organization-management)
var entraUser = await _graph.Users[request.EntraUserId]
.GetAsync(r => r.QueryParameters.Select = new[] { "id", "displayName", "mail", extensionAttr });
// Extension stores numeric GitHub user ID as string — same as GraphUserDto.GitHubId
var githubIdStr = entraUser.AdditionalData.GetValueOrDefault(extensionAttr)?.ToString();
if (string.IsNullOrEmpty(githubIdStr) || !long.TryParse(githubIdStr, out var githubId))
return SpawnResult.Error("GitHub account not linked. Please link at github-org-mgmt URL.");
// 2. Resolve GitHub username from numeric ID (Octokit)
var githubUser = await _github.User.Get(githubId);
var githubLogin = githubUser.Login; // e.g. "octocat"
// 3. Check Copilot entitlement
bool hasCopilot = false;
try
{
var seat = await _github.Organization.Copilot
.GetSeatDetails("NETWORG", githubLogin);
hasCopilot = seat.PendingCancellationDate == null;
}
catch (NotFoundException)
{
hasCopilot = false;
}
// 4. Get user's OAuth token (ghu_*) via stored refresh token
var ghToken = await _tokenStore.GetOrRefreshToken(request.EntraUserId);
// 5. Spawn container with appropriate config
return await SpawnContainer(new ContainerConfig
{
GhToken = ghToken,
CopilotEnabled = hasCopilot,
GitAuthorName = entraUser.DisplayName,
GitAuthorEmail = entraUser.Mail,
GitHubLogin = githubLogin,
});
}| Component | How AgentBox Uses It |
|---|---|
| Entra ID extension attribute | Read githubId (numeric) at spawn time → Octokit User.Get(id) → get login |
GraphUserDto model |
Same pattern: { Id, GitHubId: long?, Upn } — AgentBox reads the same attribute |
| Account linking UI | Redirect users who haven't linked yet before they can spawn |
| GitHub App | Add organization_copilot_seat_management permission for license checks |
| Microsoft Graph integration | Already configured with User.ReadWrite.All; AgentBox needs only User.Read.All |
| Sync process | Ensures org membership is current; AgentBox relies on users being org members |
-
Add Copilot billing permission to the GitHub App registration:
- Permission:
organization_copilot_seat_management→read(orwritefor JIT provisioning) - Org owners will be prompted to approve the new permission
- Permission:
-
Optional: Expose an API endpoint for Copilot license checks:
GET /api/copilot-status/{entra-user-id} → { "github_username": "octocat", "has_copilot": true, "plan_type": "business" }This avoids AgentBox needing its own GitHub App with billing permissions.
-
Optional: Add JIT seat management endpoint:
POST /api/copilot-seat/{entra-user-id}/assign DELETE /api/copilot-seat/{entra-user-id}/revoke
If you prefer not to modify github-organization-management, the AgentBox GitHub App can:
- Request
organization_copilot_seat_management: readpermission - Read the Entra ID extension attribute directly via Microsoft Graph
- Check Copilot entitlement via the GitHub API
This requires the AgentBox App to be installed at the org level with billing permissions. The trade-off is duplication of GitHub App infrastructure.
┌───────────────────────────┐ ┌──────────────────────────────────┐
│ github-org-management │ │ AgentBox API │
│ │ │ │
│ - Account linking UI │ │ - Entra ID auth │
│ - Entra ↔ GitHub mapping │ │ - Spawn form │
│ - Team/org sync │ │ - License check │
│ - [NEW] Copilot status │◄────│ - Container provisioning │
│ API endpoint │ │ - Token management │
│ │ │ │
│ GitHub App: │ │ GitHub App: │
│ - org management perms │ │ - contents, PRs, issues │
│ - [NEW] copilot billing │ │ - copilot (user access) │
│ read │ │ - OAuth device flow │
│ - members read/write │ │ │
└───────────────────────────┘ └──────────────────────────────────┘
│ │
│ Microsoft Graph │ Microsoft Graph
▼ ▼
┌─────────────────────────────────────────────────────┐
│ Entra ID (networg.com) │
│ │
│ User objects with extension attribute: │
│ extension_<app-id>_githubId = "12345678" │
│ │
└─────────────────────────────────────────────────────┘
| Question | Options | Impact |
|---|---|---|
| What to do with unlicensed users? | A) Buy seats, B) JIT provisioning, C) No Copilot, D) Copilot Free | Cost vs. feature parity |
| JIT seat provisioning cost model? | Assign on spawn / revoke on destroy vs. keep seat assigned | Billing is per-month regardless of mid-cycle changes |
| Who pays for extra seats? | Org budget vs. project budget vs. per-user chargeback | Finance decision |
| Copilot Free tier sufficient? | 2,000 completions/month, limited models | May be enough for light-use developers |
| Question | Status | Notes |
|---|---|---|
| What's the current Copilot seat count and utilization? | Check via API | GET /orgs/NETWORG/copilot/billing shows seat_breakdown |
| Can the GitHub App permission be upgraded without reinstalling? | Yes — org owners approve | GitHub prompts approval for new permissions |
Does COPILOT_GITHUB_TOKEN work with @github/copilot npm package? |
Needs testing | The npm package (copilot CLI in our Dockerfile) may have different auth paths than gh copilot |
| Copilot Business availability on GitHub Team plan? | ✅ Confirmed available | Copilot Business is for "organizations on GitHub Free or GitHub Team plan" per GitHub docs |
-
Verify
COPILOT_GITHUB_TOKENisolation in an actual AgentBox container:# In container: export COPILOT_GITHUB_TOKEN=ghu_user_a_token export GH_TOKEN=ghu_user_b_token copilot # Should authenticate as user_a gh api user # Should authenticate as user_b git push # Should authenticate as user_b
-
Verify Copilot license check API with the existing GitHub App:
# Get installation token, then: curl -H "Authorization: token ghs_xxx" \ https://api.github.com/orgs/NETWORG/members/USERNAME/copilot
-
Test the
@github/copilotnpm package auth behavior — the Dockerfile installs@github/copilotglobally. Verify it respects the same env var priority asgh copilot. -
Verify Copilot Free tier works without org seat assignment — if a user has GitHub Free, they get limited Copilot. Does this work in the CLI context without an org seat?
1. COPILOT_GITHUB_TOKEN ← Highest priority (Copilot-specific)
2. GH_TOKEN ← Shared with gh CLI
3. GITHUB_TOKEN ← Shared with gh CLI, CI/CD
4. System keychain ← From `copilot login` / `gh auth login`
5. gh auth token fallback ← Lowest priority
1. URL-embedded token ← https://x-access-token:TOKEN@github.com/...
2. GIT_ASKPASS ← Script that outputs credentials
3. credential.helper ← Configured helper (store, cache, osxkeychain)
4. SSH key ← For git@github.com:... URLs
1. GIT_AUTHOR_NAME/EMAIL + GIT_COMMITTER_NAME/EMAIL ← Environment vars
2. git config user.name/email ← .gitconfig
3. --author="Name <email>" flag ← Per-commit override
# Check if a user has Copilot (the key endpoint for AgentBox)
GET /orgs/{org}/members/{username}/copilot
# 200 = has seat, 404 = no seat
# List all seats (paginated)
GET /orgs/{org}/copilot/billing/seats?per_page=100&page=1
# Org billing summary
GET /orgs/{org}/copilot/billing
# Add seat for user
POST /orgs/{org}/copilot/billing/selected_users
Body: { "selected_usernames": ["user1"] }
# Remove seat
DELETE /orgs/{org}/copilot/billing/selected_users
Body: { "selected_usernames": ["user1"] }
# Required headers for all:
Accept: application/vnd.github+json
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28