Stitchflow
Azure DevOps logo

Azure DevOps User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedFeb 27, 2026

Summary and recommendation

Azure DevOps exposes user management across three distinct API surfaces: the Member Entitlement Management API (vsaex.dev.azure.com) for licensing and access levels, the Identity API (vssps.dev.azure.com) for group membership and permissions, and the SCIM 2.0 endpoint for Entra ID-backed provisioning.

These are not interchangeable-user IDs differ across surfaces (entitlement ID, identity ID, and member ID are separate), and mixing them without mapping is a common source of silent failures.

Authentication is via Personal Access Token (PAT) with base64 encoding in the Authorization header, or OAuth 2.0 for delegated flows; PATs are the practical default for automation. Stitchflow connects to Azure DevOps through an MCP server with ~100 deep IT/identity integrations, normalizing these fragmented surfaces into a single provisioning and audit layer.

API quick reference

Has user APIYes
Auth methodPersonal Access Token (PAT) or OAuth 2.0
Base URLOfficial docs
SCIM availableYes
SCIM plan requiredEnterprise

Authentication

Auth method: Personal Access Token (PAT) or OAuth 2.0

Setup steps

  1. Create a Personal Access Token in Azure DevOps user settings with appropriate scopes
  2. Encode PAT as base64 and include in Authorization header as 'Basic {base64_pat}'
  3. Alternatively, use OAuth 2.0 with Azure AD for delegated access

Required scopes

Scope Description Required for
vso.identity Read identity information Reading user profiles and identities
vso.identity_manage Manage identity and access Creating, updating, and removing users
vso.memberentitlementmanagement Manage member entitlements Managing user access levels and licenses

User object / data model

Field Type Description On create On update Notes
id string (UUID) Unique identifier for the user auto-generated read-only Immutable identifier
principalName string User's principal name (email or UPN) required read-only Must be unique within organization
displayName string User's display name optional writable Synced from Azure AD if using AAD integration
mailAddress string User's email address optional writable Used for notifications
accessLevel string User's access level (Stakeholder, Basic, Basic+Test Plans, etc.) optional writable Determines feature access and licensing
accountStatus string Account status (active, disabled, pending, etc.) auto-generated writable Controls user access to organization
origin string User origin (aad, msa, github, etc.) auto-detected read-only Indicates identity provider source
subjectDescriptor string Subject descriptor for authorization auto-generated read-only Used in group membership and permissions

Core endpoints

List users in organization

  • Method: GET
  • URL: https://vsaex.dev.azure.com/{organization}/_apis/userentitlements?api-version=7.1-preview.3
  • Watch out for: Requires vso.memberentitlementmanagement scope. Returns entitlements, not raw users.

Request example

GET /userentitlements?$skip=0&$top=100

Response example

{"value":[{"id":"user-id","principalName":"user@example.com","displayName":"User Name","accessLevel":{"accountLicenseType":"Basic"}}],"continuationToken":null}

Get user by ID

  • Method: GET
  • URL: https://vsaex.dev.azure.com/{organization}/_apis/userentitlements/{userId}?api-version=7.1-preview.3
  • Watch out for: userId is the entitlement ID, not the identity ID. Different from Graph API user IDs.

Request example

GET /userentitlements/{userId}

Response example

{"id":"user-id","principalName":"user@example.com","displayName":"User Name","accessLevel":{"accountLicenseType":"Basic"},"accountStatus":"active"}

Add user to organization

  • Method: POST
  • URL: https://vsaex.dev.azure.com/{organization}/_apis/userentitlements?api-version=7.1-preview.3
  • Watch out for: New users start in 'pending' status. They must accept invitation. Azure AD users are auto-added if org is AAD-backed.

Request example

{"accessLevel":{"accountLicenseType":"Basic"},"principalName":"newuser@example.com"}

Response example

{"id":"new-user-id","principalName":"newuser@example.com","accessLevel":{"accountLicenseType":"Basic"},"accountStatus":"pending"}

Update user access level

  • Method: PATCH
  • URL: https://vsaex.dev.azure.com/{organization}/_apis/userentitlements/{userId}?api-version=7.1-preview.3
  • Watch out for: Changing access levels may incur licensing costs. Some downgrades require explicit confirmation.

Request example

{"accessLevel":{"accountLicenseType":"Basic+TestPlans"}}

Response example

{"id":"user-id","accessLevel":{"accountLicenseType":"Basic+TestPlans"}}

Remove user from organization

  • Method: DELETE
  • URL: https://vsaex.dev.azure.com/{organization}/_apis/userentitlements/{userId}?api-version=7.1-preview.3
  • Watch out for: Deletion is permanent. User loses access to all projects. Cannot be undone via API.

Request example

DELETE /userentitlements/{userId}

Response example

HTTP 204 No Content

List organization members

  • Method: GET
  • URL: https://dev.azure.com/{organization}/_apis/members?api-version=7.1-preview.1
  • Watch out for: Different endpoint than userentitlements. Returns members with less detail. Use for quick lookups.

Request example

GET /members?$skip=0&$top=100

Response example

{"value":[{"id":"member-id","displayName":"User Name","uniqueName":"user@example.com"}]}

Get user identity

  • Method: GET
  • URL: https://vssps.dev.azure.com/{organization}/_apis/identities?searchFilter=General&filterValue={email}&api-version=7.1-preview.1
  • Watch out for: Identity API is separate from entitlements. Used for permissions and group membership, not licensing.

Request example

GET /identities?searchFilter=General&filterValue=user@example.com

Response example

{"value":[{"id":"identity-id","displayName":"User Name","uniqueName":"user@example.com","descriptor":"aad.xxx"}]}

Batch add users

  • Method: POST
  • URL: https://vsaex.dev.azure.com/{organization}/_apis/userentitlements/batch?api-version=7.1-preview.3
  • Watch out for: Batch operations are more efficient. Failures are per-user; partial success is possible.

Request example

{"userEntitlements":[{"principalName":"user1@example.com","accessLevel":{"accountLicenseType":"Basic"}},{"principalName":"user2@example.com","accessLevel":{"accountLicenseType":"Stakeholder"}}]}

Response example

{"value":[{"id":"user1-id","principalName":"user1@example.com","accountStatus":"pending"},{"id":"user2-id","principalName":"user2@example.com","accountStatus":"pending"}]}

Rate limits, pagination, and events

  • Rate limits: Azure DevOps enforces rate limits based on user tier and API usage patterns
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: Rate limits are per-user and per-IP. Throttling returns HTTP 429. Implement exponential backoff for retries.
  • Pagination method: offset
  • Default page size: 200
  • Max page size: 10000
  • Pagination pointer: $skip and $top
Plan Limit Concurrent
Free/Basic 1800 requests per minute per user 0
Enterprise Higher limits available 0
  • Webhooks available: Yes
  • Webhook notes: Azure DevOps supports service hooks for user-related events via subscriptions API
  • Alternative event strategy: Use Graph API change notifications or poll userentitlements endpoint for changes
  • Webhook events: User added to organization, User removed from organization, User access level changed

SCIM API status

  • SCIM available: Yes

  • SCIM version: 2.0

  • Plan required: Enterprise

  • Endpoint: https://dev.azure.com/{organization}/_apis/scim/v2

  • Supported operations: Create user (POST /Users), Read user (GET /Users/{id}), Update user (PATCH /Users/{id}), Delete user (DELETE /Users/{id}), List users (GET /Users), Create group (POST /Groups), Add user to group (PATCH /Groups/{id})

Limitations:

  • Enterprise plan required
  • Must be configured in organization settings
  • Requires Azure AD as identity provider
  • SCIM operations sync with Azure AD; direct SCIM changes may be overwritten by AAD sync
  • Group operations limited to Azure AD groups

Common scenarios

Three scenarios cover the majority of programmatic user management needs:

  • Provision a new user with Basic access: POST to https://vsaex.dev.azure.com/{organization}/_apis/userentitlements?api-version=7.1-preview.3 with principalName and accessLevel=Basic. For Entra ID-backed orgs, users are auto-added from AAD and skip the invitation flow; for MSA/GitHub identities, the user starts in pending status until they accept the invitation email. Verify final state with a GET on the same endpoint using the returned userId.

  • Downgrade a user from Basic to Stakeholder: PATCH /userentitlements/{userId} with accessLevel=Stakeholder. Some downgrades require explicit confirmation in the response; handle HTTP 400 responses that indicate a pending approval step. After downgrading, manually reduce the paid user count in Organization Settings-the API does not do this automatically.

  • Bulk provisioning via SCIM (Entra ID-backed orgs only): POST to the SCIM /Users endpoint in batches. SCIM syncs with Entra ID approximately every 40 minutes; direct SCIM writes can be overwritten by the next AAD sync cycle, so Entra ID must be treated as the authoritative source of truth. SCIM is not a replacement for the REST API-it does not cover all operations, and the REST API remains necessary for access level management and permissions.

Provision new user with Basic license

  1. POST to /userentitlements with principalName and accessLevel=Basic
  2. User receives invitation email (if not AAD-backed)
  3. User accepts invitation and gains access
  4. Verify user status via GET /userentitlements/{userId}

Watch out for: AAD-backed orgs auto-add users; manual invitations only for MSA/GitHub. New users start in 'pending' status.

Bulk import users from CSV with SCIM (Enterprise only)

  1. Ensure organization is Enterprise and AAD-backed
  2. Enable SCIM in organization settings
  3. POST batch user creation to SCIM /Users endpoint
  4. Monitor sync status; SCIM syncs with AAD every 40 minutes
  5. Verify users appear in organization

Watch out for: SCIM changes may be overwritten by AAD sync. Use AAD as source of truth for Enterprise orgs.

Downgrade user from Basic to Stakeholder to reduce costs

  1. PATCH /userentitlements/{userId} with accessLevel=Stakeholder
  2. Confirm licensing change (may require approval)
  3. User retains access but loses feature access (e.g., test plans)
  4. Verify change via GET /userentitlements/{userId}

Watch out for: Some access level changes require explicit confirmation. Downgrading may fail if user has pending work items or test plans in use.

Why building this yourself is a trap

The primary API trap is the multi-surface identity model: a user's entitlement ID (used for licensing), identity ID (used for permissions and group membership), and member ID (used in the members endpoint) are all different and not cross-referenceable without additional lookups. Automation that conflates these will silently operate on the wrong object.

A second trap is the Entra ID sync dependency: for AAD-backed organizations, removing a user via the REST API does not remove them from Entra ID groups, and those group memberships will re-provision access on the user's next sign-in-the same offboarding loop that affects manual management.

Rate limits are enforced at 1,800 requests per minute per user/IP; batch endpoints (/userentitlements/batch) are the correct pattern for bulk operations, and exponential backoff is required on HTTP 429 responses.

Finally, deleted users cannot be restored via API-there is no soft-delete or deactivation state within Azure DevOps itself; disabling access without removal requires acting on the Entra ID account directly.

Automate Azure DevOps 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.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedFeb 27, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

15Five logo

15Five

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

15Five uses a fixed role-based permission model with six predefined roles: Account Admin, HR Admin, Billing Admin, Group Admin, Manager, and Employee. No custom roles can be constructed. User management lives at Settings gear → People → Manage people p

1Password logo

1Password

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

1Password's admin console at my.1password.com covers the full user lifecycle — invitations, group assignments, vault access, suspension, and deletion — without any third-party tooling. Like every app that mixes role-based and resource-level permissions

8x8 logo

8x8

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

8x8 Admin Console supports full lifecycle user management — create, deactivate, and delete — across its X Series unified communications platform. Every app a user can access (8x8 Work desktop, mobile, web, Agent Workspace) is gated by license assignmen