Summary and recommendation
The GitHub Copilot REST API exposes seat management under /orgs/{org}/copilot/billing/ and enterprise-scope equivalents under /enterprises/{enterprise}/copilot/billing/. Authentication requires either a classic PAT with manage_billing:copilot and read:org scopes, or a GitHub App installation token with the copilot organization permission. All requests must include the X-GitHub-Api-Version: 2022-11-28 header - omitting it can produce silent failures or 404s.
Pagination is offset-style (page / per_page, max 100), with navigation via Link response headers. Standard REST rate limits apply: 5,000 requests/hour per token, with secondary limits on burst writes.
Seat-level data integrates cleanly into an identity graph - login, id, last_activity_at, last_activity_editor, plan_type, and assigning_team are all returned per seat record, enabling cross-system correlation against your IdP or HR source of truth.
API quick reference
| Has user API | Yes |
| Auth method | GitHub Personal Access Token (PAT) or GitHub App installation token; OAuth 2.0 app tokens also accepted where the GitHub Apps OAuth flow is used |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | GitHub Enterprise Cloud with Enterprise Managed Users (EMU) |
Authentication
Auth method: GitHub Personal Access Token (PAT) or GitHub App installation token; OAuth 2.0 app tokens also accepted where the GitHub Apps OAuth flow is used
Setup steps
- Create a GitHub App or generate a fine-grained PAT with the required scopes under Settings > Developer settings.
- For GitHub Apps: install the app on the target organization and generate an installation access token via POST /app/installations/{installation_id}/access_tokens.
- For PATs: generate a classic PAT with 'manage_billing:copilot' and 'read:org' scopes, or a fine-grained PAT with 'GitHub Copilot Business' organization permissions.
- Pass the token in the Authorization header: 'Authorization: Bearer
'. - Include the API version header: 'X-GitHub-Api-Version: 2022-11-28'.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| manage_billing:copilot | Read and write access to Copilot seat assignments and billing | Add seats, remove seats, list seats (classic PAT) |
| read:org | Read organization membership and team data | Resolving team slugs and org membership for seat operations |
| copilot (GitHub App permission: organization-level) | GitHub App permission granting Copilot seat management | All Copilot seat management endpoints when using a GitHub App |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| login | string | GitHub username of the seat assignee | required | immutable | Used as identifier in add/remove seat payloads |
| id | integer | GitHub user numeric ID | system-assigned | read-only | Returned in seat list responses |
| avatar_url | string (URL) | URL of the user's GitHub avatar | system-assigned | read-only | Returned in seat list responses |
| created_at | string (ISO 8601) | Timestamp when the Copilot seat was assigned | system-assigned | read-only | Seat-level field, not user account creation |
| updated_at | string (ISO 8601) | Timestamp of last seat record update | system-assigned | system-assigned | |
| pending_cancellation_date | string (ISO 8601) | null | Date seat cancellation takes effect, if scheduled | null | set on removal | Seat remains active until this date after removal request |
| last_activity_at | string (ISO 8601) | null | Last recorded Copilot activity timestamp for the user | null | system-assigned | May be null if user has not used Copilot |
| last_activity_editor | string | null | Editor/IDE used in the last Copilot activity (e.g., 'vscode') | null | system-assigned | |
| plan_type | string | Copilot plan assigned to the seat (e.g., 'business', 'enterprise') | inherited from org plan | read-only | |
| assigning_team | object | null | Team object if seat was assigned via a team, null if assigned directly | set if team-based assignment | read-only | Contains team id, name, slug |
Core endpoints
List Copilot seats for an organization
- Method: GET
- URL:
/orgs/{org}/copilot/billing/seats - Watch out for: Returns seats for the org plan only. Enterprise-level seat listing requires the enterprise endpoint. Pagination via Link header.
Request example
GET /orgs/my-org/copilot/billing/seats?per_page=50&page=1
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Response example
{
"total_seats": 2,
"seats": [
{
"created_at": "2024-01-10T00:00:00Z",
"updated_at": "2024-06-01T00:00:00Z",
"pending_cancellation_date": null,
"last_activity_at": "2024-06-10T12:00:00Z",
"last_activity_editor": "vscode",
"plan_type": "business",
"assignee": {"login": "octocat", "id": 1}
}
]
}
Get Copilot seat details for a user
- Method: GET
- URL:
/orgs/{org}/members/{username}/copilot - Watch out for: Returns 404 if the user does not have a Copilot seat in the org.
Request example
GET /orgs/my-org/members/octocat/copilot
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Response example
{
"created_at": "2024-01-10T00:00:00Z",
"updated_at": "2024-06-01T00:00:00Z",
"pending_cancellation_date": null,
"last_activity_at": "2024-06-10T12:00:00Z",
"last_activity_editor": "vscode",
"plan_type": "business",
"assignee": {"login": "octocat", "id": 1}
}
Add Copilot seats for users
- Method: POST
- URL:
/orgs/{org}/copilot/billing/selected_users - Watch out for: Only works when org Copilot access is set to 'selected users'. If set to 'all members', this endpoint returns an error. Max 100 usernames per request.
Request example
POST /orgs/my-org/copilot/billing/selected_users
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Content-Type: application/json
{"selected_usernames": ["octocat", "monalisa"]}
Response example
{
"seats_created": 2
}
Remove Copilot seats for users
- Method: DELETE
- URL:
/orgs/{org}/copilot/billing/selected_users - Watch out for: Seat remains active until end of billing cycle (pending_cancellation_date is set). Removal is not immediate.
Request example
DELETE /orgs/my-org/copilot/billing/selected_users
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Content-Type: application/json
{"selected_usernames": ["octocat"]}
Response example
{
"seats_cancelled": 1
}
Add Copilot seats for a team
- Method: POST
- URL:
/orgs/{org}/copilot/billing/selected_teams - Watch out for: Team slug must match exactly. All current team members receive seats; future members added to the team also receive seats automatically.
Request example
POST /orgs/my-org/copilot/billing/selected_teams
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Content-Type: application/json
{"selected_teams": ["engineering"]}
Response example
{
"seats_created": 5
}
Remove Copilot seats for a team
- Method: DELETE
- URL:
/orgs/{org}/copilot/billing/selected_teams - Watch out for: Cancels seats for all current team members. Pending cancellation date applies.
Request example
DELETE /orgs/my-org/copilot/billing/selected_teams
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Content-Type: application/json
{"selected_teams": ["engineering"]}
Response example
{
"seats_cancelled": 5
}
Get Copilot usage metrics for an organization
- Method: GET
- URL:
/orgs/{org}/copilot/usage - Watch out for: Usage data has a ~24-hour lag. Date range max is 28 days per request. Not a per-user breakdown; use seat list for individual last_activity_at.
Request example
GET /orgs/my-org/copilot/usage?since=2024-06-01&until=2024-06-30
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Response example
[
{
"day": "2024-06-10",
"total_suggestions_count": 1200,
"total_acceptances_count": 800,
"total_lines_suggested": 4500,
"total_lines_accepted": 3000,
"total_active_users": 15
}
]
List Copilot seats for an enterprise
- Method: GET
- URL:
/enterprises/{enterprise}/copilot/billing/seats - Watch out for: Requires enterprise-level token with manage_billing:copilot. Returns seats across all orgs in the enterprise.
Request example
GET /enterprises/my-enterprise/copilot/billing/seats?per_page=100
Authorization: Bearer <token>
X-GitHub-Api-Version: 2022-11-28
Response example
{
"total_seats": 50,
"seats": [
{
"assignee": {"login": "octocat"},
"assigning_team": null,
"plan_type": "enterprise",
"last_activity_at": "2024-06-09T08:00:00Z"
}
]
}
Rate limits, pagination, and events
- Rate limits: GitHub REST API standard rate limits apply. Authenticated requests are limited per token. Copilot endpoints share the global REST quota.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix timestamp), Retry-After (on 429). Secondary rate limits may apply for burst writes.
- Pagination method: cursor
- Default page size: 50
- Max page size: 100
- Pagination pointer: page / per_page (offset-style integers); Link header provides next/prev/last URLs
| Plan | Limit | Concurrent |
|---|---|---|
| Authenticated (PAT/App token) | 5,000 requests/hour per token | 0 |
| GitHub App installation token | 5,000 requests/hour per installation (scales with org size for large orgs) | 0 |
- Webhooks available: Yes
- Webhook notes: GitHub organization and enterprise webhooks fire events related to Copilot seat changes. No dedicated Copilot-specific webhook event type exists; seat changes are surfaced via audit log streaming rather than real-time webhooks.
- Alternative event strategy: Use GitHub Audit Log Streaming (Enterprise feature) to stream audit events to an external SIEM or storage. Poll /orgs/{org}/copilot/billing/seats for near-real-time seat state.
- Webhook events: business.add_seats (audit log event), business.remove_seats (audit log event), copilot.seat_assignment_created (audit log), copilot.seat_assignment_cancelled (audit log), copilot.seat_assignment_updated (audit log)
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: GitHub Enterprise Cloud with Enterprise Managed Users (EMU)
Endpoint: https://api.github.com/scim/v2/enterprises/{enterprise}/
Supported operations: GET /Users (list provisioned users), GET /Users/{scim_user_id} (get user), POST /Users (provision user), PATCH /Users/{scim_user_id} (update user attributes), DELETE /Users/{scim_user_id} (deprovision user), GET /Groups (list teams as groups), POST /Groups (create team), PATCH /Groups/{scim_group_id} (update team membership), DELETE /Groups/{scim_group_id} (delete team)
Limitations:
- Only available for Enterprise Managed User (EMU) organizations; not available for standard GitHub orgs.
- SCIM provisions GitHub user accounts and org membership, not Copilot seats directly - seat assignment still requires the Copilot billing API or org policy.
- Requires SAML SSO or OIDC to be configured before SCIM provisioning.
- Supported IdPs: Okta, Microsoft Entra ID (Azure AD). Google Workspace and OneLogin not officially supported.
- EMU usernames are managed by the IdP and follow the pattern {shortcode}_{username}.
- SCIM tokens must be generated by the enterprise owner using a special setup user account.
Common scenarios
Provisioning a new employee requires a sequential flow: for EMU orgs, the IdP triggers SCIM POST /Users first to create the managed account, then a separate POST /orgs/{org}/copilot/billing/selected_users call assigns the Copilot seat. SCIM and seat assignment are decoupled - SCIM provisions GitHub identity and org membership but does not automatically grant a Copilot seat.
Deprovisioning sets a pending_cancellation_date on the seat record; the seat remains active and billable until that date regardless of when the API call is made. For EMU, SCIM DELETE /Users/{scim_user_id} suspends the managed account but does not cancel Copilot billing immediately - both steps are required and must be sequenced deliberately.
For idle-seat auditing, paginate GET /orgs/{org}/copilot/billing/seats and filter on last_activity_at - null values or dates beyond a 30-day threshold are reclamation candidates. Note that last_activity_at reflects Copilot suggestion activity only; a user with the extension installed but no triggered suggestions will appear null even if actively employed. Batch removals via DELETE /orgs/{org}/copilot/billing/selected_users accept a maximum of 100 usernames per request.
Provision a new employee with a Copilot seat
- (EMU only) IdP triggers SCIM POST /Users to create the GitHub managed user account.
- Verify org membership via GET /orgs/{org}/members/{username}.
- POST /orgs/{org}/copilot/billing/selected_users with body {"selected_usernames": ["username"]} to assign a Copilot seat.
- Confirm seat via GET /orgs/{org}/members/{username}/copilot and check created_at is set.
Watch out for: Step 3 fails with 422 if the org Copilot policy is 'all members' (seats are auto-assigned) or if the user is not yet an org member.
Deprovision a departing employee and reclaim Copilot seat
- DELETE /orgs/{org}/copilot/billing/selected_users with {"selected_usernames": ["username"]} to cancel the seat.
- Note the pending_cancellation_date in the response - seat remains active until that date.
- (EMU) IdP triggers SCIM DELETE /Users/{scim_user_id} to suspend/remove the managed user account.
- Verify seat is gone after cancellation date via GET /orgs/{org}/copilot/billing/seats.
Watch out for: Billing continues until the pending_cancellation_date. Deleting the user via SCIM does not automatically cancel the Copilot seat billing immediately.
Audit active vs. inactive Copilot seat holders
- GET /orgs/{org}/copilot/billing/seats?per_page=100 and paginate through all pages using Link header.
- For each seat, inspect last_activity_at - seats with null or dates older than 30 days are candidates for reclamation.
- Cross-reference with GET /orgs/{org}/copilot/usage for aggregate active user counts.
- Build a removal list and batch DELETE /orgs/{org}/copilot/billing/selected_users (max 100 usernames per call).
Watch out for: last_activity_at reflects Copilot suggestion activity only; a user who has the extension installed but hasn't triggered a suggestion will show null even if actively employed.
Why building this yourself is a trap
The most significant API caveat is policy-state dependency: the POST and DELETE seat endpoints for selected users and teams return 422 if the org's Copilot access policy is set to 'all members' rather than 'selected users/teams'. Any automation pipeline must validate org policy state before attempting seat mutations, or handle 422 responses explicitly.
SCIM is exclusively an EMU feature - Copilot Business organizations have no SCIM endpoint. The SCIM token itself must be generated by the enterprise's setup user (a dedicated bot account), not a standard enterprise owner PAT, which is a non-obvious credential management requirement.
Usage metrics from /orgs/{org}/copilot/usage carry a ~24-hour data lag and provide no per-user granularity, making them unsuitable for real-time seat state; use the seat list endpoint with last_activity_at for individual-level signals instead.
Enterprise-scope endpoints (/enterprises/{enterprise}/...) require a token scoped to the enterprise, not an org-level token - mixing these silently returns incomplete data or auth errors depending on the endpoint.
Automate Github Copilot workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.