Summary and recommendation
Vercel's REST API exposes full team member lifecycle management under the /v1/teams/{teamId}/members and /v2/teams/{teamId}/members endpoint families, authenticated via Bearer token (personal access token or OAuth 2.0). The API is versioned per-endpoint - v1 and v2 are not interchangeable across all operations - so callers must verify the correct version for each call.
Most member endpoints require the numeric teamId (team_xxx), not the team slug; retrieve it via GET /v2/teams before building any identity graph that maps Vercel membership to upstream directory records. Rate limits are communicated via X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix timestamp) headers; on 429, a Retry-After header is included.
Pro tokens are limited to approximately 3,000 requests/minute on general REST endpoints.
API quick reference
| Has user API | Yes |
| Auth method | Bearer token (personal access token or OAuth 2.0 access token) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise (native); Pro with SCIM add-on ($150/month) |
Authentication
Auth method: Bearer token (personal access token or OAuth 2.0 access token)
Setup steps
- Navigate to Vercel Dashboard → Settings → Tokens to create a personal access token.
- Select token scope (full account or specific team) and expiration.
- Pass the token in the Authorization header: 'Authorization: Bearer
'. - For OAuth 2.0: register an integration at https://vercel.com/docs/integrations, implement the authorization code flow, and exchange the code for an access token at https://api.vercel.com/v2/oauth/access_token.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| team | Read and write access to team settings and membership | List, invite, update, and remove team members |
| user | Read access to the authenticated user's profile | GET /v2/user |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| uid | string | Unique user identifier | system-generated | immutable | Used as path param in member endpoints |
| string | User's email address | required (for invite) | not updatable via API | Used as invite target | |
| username | string | Vercel username/handle | optional | read-only via team member API | |
| name | string | Display name of the user | optional | read-only via team member API | |
| avatar | string | Avatar image hash | system-generated | read-only | Construct URL: https://vercel.com/api/www/avatar/ |
| role | string (enum) | Team role: OWNER, MEMBER, DEVELOPER, BILLING, VIEWER | required | patchable | OWNER role can only be set by existing owners |
| confirmed | boolean | Whether the user has confirmed their email | system-set | read-only | |
| joinedFrom | object | Source of team join (e.g., invite, SAML, SCIM) | system-set | read-only | Contains origin field |
| createdAt | number (Unix ms) | Timestamp when user joined the team | system-set | read-only | |
| accessRequestedAt | number (Unix ms) | Timestamp of access request if pending | system-set | read-only |
Core endpoints
Get authenticated user
- Method: GET
- URL:
https://api.vercel.com/v2/user - Watch out for: Returns the token owner's profile only; not a directory of all users.
Request example
GET /v2/user
Authorization: Bearer <token>
Response example
{
"user": {
"uid": "usr_abc123",
"email": "user@example.com",
"username": "jdoe",
"name": "Jane Doe"
}
}
List team members
- Method: GET
- URL:
https://api.vercel.com/v2/teams/{teamId}/members - Watch out for: Requires a team-scoped token or OAuth token with team access. Personal tokens on Hobby plan have no team context.
Request example
GET /v2/teams/team_xyz/members?limit=20
Authorization: Bearer <token>
Response example
{
"members": [{"uid":"usr_1","email":"a@b.com","role":"MEMBER"}],
"pagination": {"count":1,"next":null}
}
Invite user to team
- Method: POST
- URL:
https://api.vercel.com/v1/teams/{teamId}/members - Watch out for: Sends an email invite; user is not active until they accept. SCIM-provisioned users bypass this flow.
Request example
POST /v1/teams/team_xyz/members
Content-Type: application/json
{"email":"new@example.com","role":"MEMBER"}
Response example
{
"uid": "usr_new123",
"username": null,
"email": "new@example.com",
"role": "MEMBER"
}
Update team member role
- Method: PATCH
- URL:
https://api.vercel.com/v1/teams/{teamId}/members/{userId} - Watch out for: Only OWNER-role callers can promote another member to OWNER.
Request example
PATCH /v1/teams/team_xyz/members/usr_abc123
Content-Type: application/json
{"role":"DEVELOPER"}
Response example
{
"uid": "usr_abc123",
"role": "DEVELOPER"
}
Remove team member
- Method: DELETE
- URL:
https://api.vercel.com/v1/teams/{teamId}/members/{userId} - Watch out for: Removing the last OWNER is blocked. SCIM-managed users should be deprovisioned via IdP, not this endpoint.
Request example
DELETE /v1/teams/team_xyz/members/usr_abc123
Authorization: Bearer <token>
Response example
{
"id": "usr_abc123"
}
Get team member
- Method: GET
- URL:
https://api.vercel.com/v2/teams/{teamId}/members/{userId} - Watch out for: userId can be the uid or username.
Request example
GET /v2/teams/team_xyz/members/usr_abc123
Authorization: Bearer <token>
Response example
{
"uid": "usr_abc123",
"email": "a@b.com",
"role": "MEMBER",
"confirmed": true
}
List access request (pending invites)
- Method: GET
- URL:
https://api.vercel.com/v1/teams/{teamId}/request - Watch out for: Only returns users who requested access, not email-invited pending users.
Request example
GET /v1/teams/team_xyz/request
Authorization: Bearer <token>
Response example
{
"accessRequests": [{"uid":"usr_pending","email":"p@b.com"}]
}
Join team (accept invite)
- Method: POST
- URL:
https://api.vercel.com/v1/teams/{teamId}/members/teams/join - Watch out for: inviteCode is extracted from the invite email link. Not applicable for SCIM-provisioned users.
Request example
POST /v1/teams/team_xyz/members/teams/join
Content-Type: application/json
{"inviteCode":"<code>"}
Response example
{
"uid": "usr_abc123",
"role": "MEMBER"
}
Rate limits, pagination, and events
- Rate limits: Vercel enforces per-token rate limits. Limits vary by plan and endpoint category. Rate limit state is communicated via response headers.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Headers returned: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix timestamp). On 429, Retry-After header is included. Exact per-endpoint limits are not fully published; the above are approximate general limits from community and official sources.
- Pagination method: cursor
- Default page size: 20
- Max page size: 100
- Pagination pointer: until (cursor from previous response pagination.next), limit
| Plan | Limit | Concurrent |
|---|---|---|
| Hobby | 60 requests/minute (general REST API) | 0 |
| Pro | 3,000 requests/minute (general REST API) | 0 |
| Enterprise | Higher limits negotiated; contact Vercel | 0 |
- Webhooks available: Yes
- Webhook notes: Vercel supports webhooks for deployment and project events. There are no dedicated user/member lifecycle webhook events (e.g., member-added, member-removed) documented in the official REST API as of early 2025.
- Alternative event strategy: Poll GET /v2/teams/{teamId}/members on a schedule to detect membership changes, or use SCIM provisioning events via your IdP.
- Webhook events: deployment.created, deployment.succeeded, deployment.failed, deployment.cancelled, deployment.error, project.created, project.removed, integration-configuration.removed, domain.created
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise (native); Pro with SCIM add-on ($150/month)
Endpoint: https://api.vercel.com/scim/v2
Supported operations: GET /Users – list provisioned users, GET /Users/{id} – get single user, POST /Users – provision user, PUT /Users/{id} – replace user attributes, PATCH /Users/{id} – update user (activate/deactivate), DELETE /Users/{id} – deprovision user, GET /Groups – list groups (teams), POST /Groups – create group, PATCH /Groups/{id} – add/remove group members, DELETE /Groups/{id} – delete group
Limitations:
- Requires SSO (SAML) to be configured before enabling SCIM.
- Powered by WorkOS; supported IdPs are Okta, Microsoft Entra ID (Azure AD), and Google Workspace.
- SCIM-provisioned users cannot be managed via the standard REST member endpoints without risk of sync conflicts.
- Group-to-team mapping behavior depends on IdP group push configuration.
- Role mapping from IdP groups to Vercel roles requires manual configuration in the Vercel dashboard.
Common scenarios
Three scenarios cover the majority of programmatic access management needs:
Invite and role-assign: POST /v1/teams/{teamId}/members with {"email": "user@example.com", "role": "DEVELOPER"}. The user is not active until they accept the email invite; poll GET /v2/teams/{teamId}/members filtering by email and confirmed=true to detect acceptance. Do not use this flow if SCIM is active - provision through the IdP to avoid sync conflicts.
Bulk audit: GET /v2/teams/{teamId}/members?limit=100, then paginate via the until cursor from pagination.next until exhausted. Collect uid, email, role, confirmed, joinedFrom.origin, and createdAt per member to build or refresh an identity graph against your directory of record. Default page size is 20; always set limit=100 to minimize round-trips.
SCIM deprovision (Enterprise or Pro add-on): Remove the user from the Vercel application assignment in your IdP (Okta, Entra ID, or Google Workspace). The IdP sends SCIM PATCH /Users/{id} with active=false or DELETE /Users/{id} to https://api.vercel.com/scim/v2. Verify via GET /v2/teams/{teamId}/members. Never call the REST DELETE member endpoint in parallel with SCIM - doing so can produce inconsistent state on the next IdP sync cycle.
Programmatically invite and assign a role to a new team member
- Obtain a team-scoped Bearer token from Vercel Dashboard → Settings → Tokens.
- Retrieve the teamId via GET https://api.vercel.com/v2/teams (match on slug).
- POST https://api.vercel.com/v1/teams/{teamId}/members with body {"email":"user@example.com","role":"DEVELOPER"}.
- User receives an email invite; poll GET /v2/teams/{teamId}/members filtering by email to confirm confirmed=true after acceptance.
Watch out for: The user is not active until they accept the invite. If SSO/SCIM is enabled, provision through the IdP instead to avoid sync conflicts.
Bulk-list and audit all team members
- GET https://api.vercel.com/v2/teams/{teamId}/members?limit=100.
- If pagination.next is non-null, repeat with ?until={pagination.next}&limit=100.
- Collect uid, email, role, confirmed, joinedFrom.origin, and createdAt for each member.
- Compare against your identity provider's directory to identify orphaned accounts.
Watch out for: Default page size is 20; always set limit=100 to minimize requests. Cursor-based pagination means you cannot jump to an arbitrary page.
Deprovision a user via SCIM (Enterprise/Pro add-on)
- Ensure SAML SSO and SCIM are configured in Vercel Dashboard → Settings → Security.
- In your IdP (Okta/Entra/Google Workspace), remove the user from the Vercel application assignment.
- The IdP sends a SCIM PATCH /Users/{id} with active=false (or DELETE /Users/{id}) to https://api.vercel.com/scim/v2.
- Verify removal by calling GET https://api.vercel.com/v2/teams/{teamId}/members and confirming the user is absent.
Watch out for: Do not also call the REST DELETE member endpoint; doing so while SCIM is active can cause inconsistent state on the next IdP sync cycle.
Why building this yourself is a trap
The most consequential caveat is SCIM/REST co-management: if SCIM is active, REST-side member changes may be silently overwritten on the next IdP sync. Teams that mix both management planes without a clear ownership boundary will encounter ghost memberships or unexpected re-provisioning. Establish a single authoritative plane before automating.
Webhooks do not cover member lifecycle events - there are no documented member-added or member-removed webhook triggers as of early 2025. Detecting membership changes programmatically requires polling GET /v2/teams/{teamId}/members on a schedule, which has rate limit implications at scale.
Personal access tokens carry the full permissions of the creating user and are not OAuth-scoped; treat them as high-privilege credentials and rotate on a defined schedule.
Automate Vercel 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.