Mass Invite
The mass invite feature sends GitHub organization invitations to a list of usernames in a single request. The UI supports three import methods: GitHub user search, JSON array paste, and CSV/XLSX file upload.
Endpoint
POST /api/organizations/[login]/mass-invite
How It Works
- The server resolves each username via
octokit.rest.users.getByUsername({ username })— a REST call using the installation token (not the user’s OAuth token). - For each resolved user ID, it calls
octokit.rest.orgs.createInvitation({ org, invitee_id: user.data.id, role: 'direct_member' }). - All invitations run concurrently via
Promise.allSettled(uniqueLogins.map(...))— there is no per-user batching or rate limiting. - The endpoint is fully synchronous — it blocks until all
Promise.allSettledresults return, then responds.
Request Format
{
"userLogins": ["username1", "username2", "username3"]
}Response Format
{
"success": ["username1", "username3"],
"failed": [{ "login": "username2", "error": "Not Found" }]
}The key is success (not succeeded). Each failed entry includes the login and a human-readable error string.
Limits and Behavior
| Property | Detail |
|---|---|
| Max users per request | 50 (returns 413 if exceeded) |
| Body field name | userLogins (not logins) |
| Deduplication | new Set(normalizedLogins) — string-sensitive |
| Role | Always 'direct_member' — no owner or billing-manager |
| Concurrency | All users simultaneously via Promise.allSettled |
| Execution | Fully synchronous — returns results immediately |
| Authentication | Active session + org admin + GitHub App installed on org |
UI Import Methods
The MassInvite component (components/organization/MassInvite.tsx) provides three import panels:
- GitHub user search — live search via
useSearchUsersQuery; select users one at a time; shows avatar + login. - JSON textarea — paste an array of strings or
[{login},{username}]objects. Parsed client-side. - File upload — accepts
.csv,.xlsx,.xlsvia thexlsxlibrary. Tries column detection:username,login,github,github_username. Falls back to raw flat values for headerless files.
Selected users appear as removable tags. One POST sends all selected users.
Common Failure Reasons
'already a member'— user is already in the org'Not Found'— GitHub username does not exist'insufficient permissions'— the GitHub App lacks required permissions or is not installed
Admin Check Caveat
The org admin check (viewerCanAdminister) is snapshotted at sign-in time from GitHub’s ViewerProfile GraphQL query and stored in organization_memberships. If a user is promoted to admin on GitHub after signing in, they must sign out and sign back in before the invite endpoint accepts their requests. There is no background refresh of the admin flag.
Prerequisites
- The GitHub App must be installed on the target organization.
- You must be signed in with an account that has org admin permissions.
- The target org must be the same org the App is installed on.
Team Membership Management
Bulk-add multiple users to a GitHub team in a single request.
Endpoint
POST /api/organizations/[login]/teams/bulk-add
Request Format
{
"userLogins": ["username1", "username2"],
"teamSlug": "frontend"
}Response Format
{
"success": ["username1"],
"invited": [],
"failed": [{ "login": "username2", "error": "already a member" }]
}Limits
| Property | Detail |
|---|---|
| Max users per request | 50 (returns 413 if exceeded) |
| Execution | Sequential (loop — not Promise.allSettled) |
| Authentication | Session + org admin + GitHub App installed |
Auth note: This route uses
getRequestSessiondirectly with an inline admin check instead of the standardrequireOrganizationAdminguard. The behavior is equivalent but inconsistent with most other admin routes.
UI
The TeamManagement component provides a team selector dropdown (populated via useOrgTeamsQuery) and a textarea for whitespace/comma-separated usernames. Submit via useBulkTeamMembersMutation.
Bulk Role Management
Set multiple organization members to member or admin in a single request.
Endpoint
POST /api/organizations/[login]/bulk-role
Request Format
{
"userLogins": ["username1", "username2"],
"role": "admin"
}role must be 'member' or 'admin'.
Response Format
{
"success": ["username1"],
"invited": [],
"failed": [{ "login": "username2", "error": "user not found in org" }]
}Limits
| Property | Detail |
|---|---|
| Max users per request | 50 |
| Execution | Sequential (loop — not Promise.allSettled) |
| Authentication | Session + org admin + GitHub App installed |
Auth note: Same non-standard auth pattern as teams/bulk-add — uses
getRequestSession+ inline admin check instead ofrequireOrganizationAdmin.
UI
The BulkRoleManagement component provides a role selector (member/admin), username textarea, and a two-step confirmation flow. First, review the count and target role; then confirm to submit. Uses useBulkRoleMutation.
Installation Requirement
All three mutation endpoints require the GitHub App to be installed on the organization. If the App is not installed, or the user’s session has no linked installation, the endpoint returns 403.
Run /api/install/start to link an installation to your session before using these endpoints.
Related
- Organizations (backend) — full endpoint reference
- Installation — how GitHub App installations work
- API Reference — complete route map