🐰 New: Triage — a self-updating cross-repository queue that prioritizes pull requests by value and risk, so you always know what to review next. Now in beta! Explore Triage →
Learn how to integrate CodeRabbit with Azure DevOps repositories using a personal access token or a Microsoft Entra service principal for automated AI-powered code reviews and pull request analysis.
Only administrators can set up this integration. Before you begin, confirm that you are a CodeRabbit organization administrator and have either Project Administrator or Project Collection Administrator permissions in Azure DevOps. If you don’t have both roles, ask the appropriate administrators to complete the setup. Select the authentication tab that matches your setup for the full requirements.
CodeRabbit enhances your Azure DevOps development workflow by providing:
Automated code reviews for pull requests
AI-powered suggestions and One-Click Fixes
Interactive assistance through the CodeRabbit bot for real-time feedback
To interact with CodeRabbit in pull request comments, use @coderabbitai — or the username of the service account you configure below, if it differs.
A Microsoft Entra service principal is the recommended authentication method, and the one new Azure DevOps organizations and new Enterprise SSO workspaces use, with a client ID and client secret. Existing PAT-based organizations and workspaces keep their current authentication flow. Select the tab that matches your setup.
Azure DevOps write operations require Identity: Read access (vso.identity) in addition to the relevant resource-specific write permissions. Grant this scope to the credential CodeRabbit uses so it can resolve identities before posting comments, updating pull requests, or performing other write operations.
A service principal is the authentication method for new Azure DevOps organizations and new Enterprise SSO workspaces. CodeRabbit authenticates as a Microsoft Entra application that you add to your Azure DevOps organization. Existing PAT-based setups keep their current authentication flow.
A service principal is added to your Azure DevOps organization in the same way as a user and is authorized through Azure DevOps permissions. Your organization’s Microsoft Entra policies may require an administrator to approve CodeRabbit’s Azure connection.
You must be a CodeRabbit organization administrator to submit the credentials. If you are not an organization administrator, ask one to complete the CodeRabbit step after the Microsoft Entra and Azure DevOps administrators configure the service principal. The CodeRabbit role does not grant Microsoft Entra or Azure DevOps permissions.At a high level, you will:
Create a Microsoft Entra app registration and a client secret.
Add the app’s service principal to your Azure DevOps organization.
Grant it the Azure DevOps permissions CodeRabbit needs.
Enter the app’s Client ID and Client Secret in CodeRabbit.
CodeRabbit usually detects your Microsoft Entra tenant from the Azure DevOps organization. If detection fails, the setup asks you to connect Azure once; the client ID and client secret stay in the form while you complete the connection.
In the app registration, go to Certificates & secrets → Client secrets, then select New client secret.
2
Create and copy the secret
Add a description and an expiration, select Add, then copy the secret Value immediately. It is shown only once. You enter this in CodeRabbit as the Client Secret.
Client secrets expire. Set a reminder to create a new secret and update it in CodeRabbit before the expiration date, or reviews stop until you provide a new one.
Step 3: Add the service principal to your organization
1
Open Users in organization settings
In Azure DevOps, go to Organization settings → Users, then select Add users.
2
Add the service principal
In the Users field, search for the app registration by its name (or paste its Application (client) ID) and select it. Set the Access level to Basic, add it to at least one project, and select Add.
Adding a service principal to an organization requires the organization to be connected to Microsoft Entra ID. See Prerequisites for a service principal.
CodeRabbit needs two kinds of Azure DevOps permission: standard repository and work-item access, and permission to manage service hooks (webhooks). Grant both on each project whose repositories CodeRabbit reviews.
Repository and work item access
We recommend adding the service principal to each project’s Contributors group, which grants everything CodeRabbit needs for reviews in a single step: go to Project settings → Permissions → Contributors → Members → Add, and add the service principal. This is a recommendation, not a requirement — you can give the service principal the equivalent permissions any way you prefer (a custom group or explicit permission grants), as long as it ends up with the access below. Make sure every project CodeRabbit reviews has these capabilities:
CodeRabbit capability
Azure DevOps permission (included in Contributors)
Read repository content, pull requests, and files
Code — Read
Post and update review comments and statuses, push branches, and open pull requests
Code — Contribute, Contribute to pull requests, Create branch
Read and comment on work items
Work Items — Read & write
Resolve pull request authors and reviewers
Identity — Read (vso.identity)
Service hooks (webhook) permission
CodeRabbit installs a webhook to receive pull request events, which requires the Edit Subscriptions permission on Service Hooks. At the time of writing, this permission cannot be granted from the Azure DevOps UI and must be set per project with the Azure CLI — install the extension with az extension add --name azure-devops and sign in as an administrator.
Without the Edit Subscriptions permission, CodeRabbit connects successfully but cannot install the webhooks that trigger reviews, so pull requests are never reviewed. This permission is project-scoped — repeat these steps for every project CodeRabbit reviews.
1
Get the project ID
az devops project show \ --org "https://dev.azure.com/ORG_NAME" \ --project "PROJECT_NAME" \ --query id \ -o tsv
Find your service principal in the response and copy its descriptor value. The API can return multiple pages. If the service principal is not in the first response, follow the x-ms-continuationtoken response header and repeat the request with --query-parameters continuationToken=TOKEN until the header is absent. See the Service Principals - List API.
Replace SP_DESCRIPTOR with the descriptor from the previous step and PROJECT_ID with the ID from the first step. The namespace ID is the fixed identifier for the Service Hooks security namespace; --allow-bit 3 grants View and Edit Subscriptions, and --merge true preserves the service principal’s other permissions.
Step 5: Connect the service principal in CodeRabbit
1
Open the Azure DevOps settings
In the CodeRabbit application, start Azure DevOps onboarding for a new organization. For an Enterprise SSO workspace, open the workspace provider settings instead. You must be a CodeRabbit organization administrator.
2
Enter the credentials
Paste the Client ID and Client Secret from Steps 1 and 2. If CodeRabbit cannot detect the Microsoft Entra tenant, follow the prompt to connect Azure once, then return to the form and submit the retained credentials. CodeRabbit validates the service principal before saving it. If validation fails, recheck the permissions in Step 4.
3
Reuse a matching credential when available
Enterprise SSO workspace onboarding can reuse an existing organization credential when its authentication type matches the workspace setup. Credentials entered in the form take precedence over a selected reusable credential.
New service-principal onboarding does not fall back to a Personal Access Token. Existing PAT-based setups continue to work, but replacing a working credential with a service principal is currently available only for approved migrations. Contact CodeRabbit support if you need to migrate an existing setup.
Before doing anything else, verify that your Azure DevOps account has either
Project Administrator or Project Collection Administrator permissions.
Only administrators can complete this integration. If you don’t have these
permissions, ask an administrator to perform the setup. See
Prerequisites for the complete list of requirements.
2
Create a dedicated service account
In Azure DevOps, create a new user to act as a dedicated service account for
CodeRabbit. Using a separate account—rather than a personal one—gives you
better access control and makes CodeRabbit’s activity easy to recognize. See
Recommendations for naming and configuration best
practices.
3
Generate a Personal Access Token for the service account
Logged in as the service account, generate a Personal Access Token (PAT).
CodeRabbit uses this token to interact with the Azure DevOps API on behalf of
the service account. Follow the detailed steps in
Generating a Personal Access Token,
then save the token securely—it is only displayed once.
Return to the CodeRabbit application and log in
with your account. You’ll be redirected to a page displaying Microsoft Apps
consent requests.
CodeRabbit admin consent page for Azure DevOps integration
Once the applications are approved, click Continue. On the “Onboarding”
page, select the organization you wish to integrate with. Then, on the “Azure
DevOps User” page, enter the Personal Access Token you generated for the
service account.
CodeRabbit Azure DevOps integration settings page
5
Install CodeRabbit on your repositories
To install CodeRabbit and its webhooks on a repository, toggle the switch on
the “Repositories” page. This registers the webhooks CodeRabbit needs to
receive pull request events.
For an existing PAT-based setup, CodeRabbit uses the Personal Access Token to interact with the Azure DevOps API and post reviews on pull requests.We recommend creating a dedicated service account in Azure DevOps and generating the Personal Access Token for that account, following our recommendations. Generating the token from a dedicated account—rather than a personal one—keeps CodeRabbit’s access scoped and its activity easy to identify.
New Enterprise SSO workspaces use a service principal during workspace onboarding or from workspace provider settings. Established PAT-only workspaces continue to receive the PAT form.Service-principal onboarding can reuse an existing organization credential only when the authentication types match: service principal with service principal, or PAT with PAT. Credentials entered in the form take precedence over a selected reusable credential. If CodeRabbit cannot detect the Microsoft Entra tenant, connect Azure once; the credentials already entered remain in the form.For an established PAT-based Multi-Org rollout, configure one workspace-level Azure DevOps personal access token for the dedicated CodeRabbit service user. Open the Azure DevOps user settings in CodeRabbit, then save a workspace personal access token scoped to the Azure DevOps organizations attached to the workspace.The workspace token should have access to every Azure DevOps organization attached to the workspace, with Code read and write access and Pull Request scopes. CodeRabbit stores the token encrypted. For Enterprise SSO workspaces, PR Review uses the workspace credential first and falls back to an eligible organization credential. For linked non-SSO workspaces, it uses an eligible organization credential first and falls back to the workspace credential when the organization credential is absent or unusable.
Pull requests opened by the dedicated service user are not reviewed by CodeRabbit, because the reviewer cannot review its own pull requests.
Existing PAT-based setups use a Personal Access Token to interact with the Azure DevOps API. Generate the token from an existing Azure DevOps user.For an existing PAT-based setup, add the Personal Access Token after the onboarding screen.
Azure DevOps provides an option to generate a personal access token for a user. Follow these steps to generate the token:
1
Log in to Azure DevOps
Log in as the user intended for CodeRabbit reviews.
2
Access Personal Access Tokens
Next to your avatar on the header’s right side is a settings icon. Click it,
then choose Personal Access Tokens.
3
Create new token
Click New Token.
4
Configure token scope
Select the organization you want to use the token with or select All
accessible organizations.
5
Set token details
Enter a name and an expiry date for the token.
6
Grant permissions
Grant the following permissions:
Read, write, & manage access to Work Items and Code (required for code reviews)
Read access to Identity (required; the vso.identity scope is used to resolve pull request authors and reviewers and is needed for Azure DevOps write operations)
Read access to Build (optional, for CodeRabbit Team pipeline failure remediation)
7
Create and save token
Click Create and save the generated token securely as it will only be
displayed once.
Token Management:
Personal access tokens have expiration dates. Ensure the expiration date covers the duration of your intended use of the CodeRabbit app
Code reviews will be attributed to the owner of the personal access token
If the personal access token expires, you can add a new one via the CodeRabbit UI:
Navigate to the Azure DevOps User page in the sidebar
Add the new token and click Save
CodeRabbit Azure DevOps personal access token creation form
If you install repositories from the Repositories page, CodeRabbit creates the Azure DevOps service hooks automatically. Use this flow when you want to create the service hooks yourself — for example, when the account connected to CodeRabbit cannot read every project, or when you want to script the setup across many repositories — or when you need to rotate the shared webhook secret.
1
Open Webhook Secret settings
In the CodeRabbit app, open Account and select Webhook Secret from
the sidebar.
2
Copy the webhook URL and organization ID
Use the Webhook URL and Organization ID fields on that page to copy
the endpoint your service hooks should call and the value they must send in
the x-coderabbit-org header.
3
Save the webhook secret
Enter the secret that Azure DevOps should send with webhook deliveries and
save it in CodeRabbit. Service hooks send it in the
x-coderabbit-webhook-secret header.
4
Create the service hooks
Create Web Hooks service hook subscriptions that POST to the copied
webhook URL, sending both HTTP headers, for these events:
Pull request created, Pull request updated, Pull request merge
attempted and Pull request commented on for each repository
Work item created, Work item updated and Work item commented
on once per project
You can create them in the Azure DevOps project settings UI or with the
script below.
When a valid webhook arrives for a repository that is not yet installed in
CodeRabbit, the repository is registered automatically — you do not need to
add it from the Repositories page.
If you change an existing webhook secret, CodeRabbit attempts to update existing CodeRabbit-managed Azure DevOps service hooks automatically. If a service hook was created manually, or if an automatic refresh fails, rerun the script below with the new secret and the -f flag (it replaces existing hooks that point at the CodeRabbit webhook URL) or update the headers directly in Azure DevOps.
For administrators managing many Azure DevOps repositories, use a script to create the service hooks across a repository, a project, or every project in the organization. Below is a sample script that requires:
Your Azure DevOps organization name
The webhook URL, organization ID, and webhook secret from the steps above
An Azure DevOps personal access token that can manage service hooks. Full access is simplest; a scoped token needs Project and Team: Read (to list projects), Code: Read (to list repositories), Work Items: Read (for the work-item subscriptions), and Service Hooks: Read & manage
azure-devops-webhook.sh script
#!/usr/bin/env bash## azure-devops-webhook.sh# Create CodeRabbit service hooks for one repository, one project,# or every project in an Azure DevOps organizationset -euo pipefailusage() { cat <<EOFUsage: $0 -o <ado-org> -u <webhook-url> -c <coderabbit-org-id> \\ [-s <webhook-secret>] [-t <pat>] [-p <project> [-r <repository-id>]] [-f] [-v]Required: -o Azure DevOps organization name (https://dev.azure.com/<org>) -u Webhook URL copied from the CodeRabbit Webhook Secret page -c CodeRabbit organization ID copied from the same pageSecret: -s Webhook secret saved on the CodeRabbit Webhook Secret page. Prefer setting \$CODERABBIT_WEBHOOK_SECRET instead so the secret stays out of your shell history and process listAuthentication: -t Personal access token able to manage service hooks. If omitted, \$AZURE_DEVOPS_TOKEN is usedScope (default: every project in the organization): -p Project ID or name -r Repository ID (requires -p)Options: -f Replace existing CodeRabbit hooks instead of skipping them (use after rotating the webhook secret) -v Verbose outputEOF exit 1}ORG="" HOOK_URL="" CR_ORG_ID=""HOOK_SECRET="${CODERABBIT_WEBHOOK_SECRET:-}"TOKEN="${AZURE_DEVOPS_TOKEN:-}" PROJECT="" REPO="" FORCE=false VERBOSE=falsewhile getopts "o:u:s:c:t:p:r:fv" opt; do case "$opt" in o) ORG=$OPTARG ;; u) HOOK_URL=$OPTARG ;; s) HOOK_SECRET=$OPTARG ;; c) CR_ORG_ID=$OPTARG ;; t) TOKEN=$OPTARG ;; p) PROJECT=$OPTARG ;; r) REPO=$OPTARG ;; f) FORCE=true ;; v) VERBOSE=true ;; *) usage ;; esacdone[[ -z $ORG || -z $HOOK_URL || -z $CR_ORG_ID ]] && usage[[ -n $REPO && -z $PROJECT ]] && usageif [[ -z $HOOK_SECRET ]]; then echo "[ERROR] No webhook secret provided. Use -s or set \$CODERABBIT_WEBHOOK_SECRET" >&2 exit 1fiif [[ -z $TOKEN ]]; then echo "[ERROR] No access token provided. Use -t or set \$AZURE_DEVOPS_TOKEN" >&2 exit 1fiAPI="https://dev.azure.com/${ORG}"API_VERSION="6.0-preview"AUTH_HEADER="Authorization: Basic $(printf ':%s' "$TOKEN" | base64 | tr -d '\n')"CURL_BASE=(curl -sSf --header "$AUTH_HEADER")REVIEW_EVENTS=( "git.pullrequest.created" "git.pullrequest.updated" "git.pullrequest.merged" "ms.vss-code.git-pullrequest-comment-event")WORKITEM_EVENTS=( "workitem.created" "workitem.updated" "workitem.commented")HTTP_HEADERS="x-coderabbit-org:${CR_ORG_ID}x-coderabbit-webhook-secret:${HOOK_SECRET}"CREATED=0 EXISTING=0 REPLACED=0 FAILED=0EXISTING_HOOKS=$("${CURL_BASE[@]}" \ "${API}/_apis/hooks/subscriptions?consumerId=webHooks&api-version=${API_VERSION}")existing_hook_ids() { local event_type=$1 project_id=$2 repo_id=$3 jq -r --arg event "$event_type" --arg url "$HOOK_URL" \ --arg project "$project_id" --arg repo "$repo_id" \ '.value[] | select( .eventType == $event and .consumerInputs.url == $url and .publisherInputs.projectId == $project and ((.publisherInputs.repository // "") == $repo) ) | .id' <<<"$EXISTING_HOOKS" 2>/dev/null}create_hook() { local event_type=$1 project_id=$2 repo_id=$3 local existing_ids existing_ids=$(existing_hook_ids "$event_type" "$project_id" "$repo_id") if [[ -n $existing_ids && "$FORCE" != "true" ]]; then [[ "$VERBOSE" == "true" ]] && echo "[INFO] Hook already exists: $event_type ($project_id ${repo_id:-project-wide})" >&2 EXISTING=$((EXISTING + 1)) return 0 fi local publisher_inputs if [[ -n $repo_id ]]; then publisher_inputs=$(jq -n --arg p "$project_id" --arg r "$repo_id" \ '{projectId: $p, repository: $r, branch: ""}') else publisher_inputs=$(jq -n --arg p "$project_id" '{projectId: $p}') fi local payload payload=$(jq -n \ --arg event "$event_type" \ --arg url "$HOOK_URL" \ --arg headers "$HTTP_HEADERS" \ --argjson publisher "$publisher_inputs" \ '{ consumerActionId: "httpRequest", consumerId: "webHooks", consumerInputs: {url: $url, httpHeaders: $headers}, eventType: $event, publisherId: "tfs", publisherInputs: $publisher, scope: 1 }') if "${CURL_BASE[@]}" --request POST \ --header "Content-Type: application/json" \ --data "$payload" \ "${API}/_apis/hooks/subscriptions?api-version=${API_VERSION}" >/dev/null; then if [[ -n $existing_ids ]]; then local old_id deleted_all=true while IFS= read -r old_id; do [[ -z $old_id ]] && continue if ! "${CURL_BASE[@]}" --request DELETE \ "${API}/_apis/hooks/subscriptions/${old_id}?api-version=${API_VERSION}" >/dev/null; then echo "[ERROR] Created replacement but failed to delete old hook $old_id for $event_type" >&2 deleted_all=false fi done <<<"$existing_ids" if [[ "$deleted_all" == "true" ]]; then [[ "$VERBOSE" == "true" ]] && echo "[INFO] Replaced: $event_type ($project_id ${repo_id:-project-wide})" >&2 REPLACED=$((REPLACED + 1)) else FAILED=$((FAILED + 1)) fi else [[ "$VERBOSE" == "true" ]] && echo "[INFO] Created: $event_type ($project_id ${repo_id:-project-wide})" >&2 CREATED=$((CREATED + 1)) fi else echo "[ERROR] Failed to create $event_type for project $project_id repo ${repo_id:-<none>}" >&2 FAILED=$((FAILED + 1)) fi}process_project() { local project_id=$1 local repos repos=$("${CURL_BASE[@]}" \ "${API}/${project_id}/_apis/git/repositories?api-version=${API_VERSION}") || { echo "[ERROR] Failed to list repositories for project $project_id" >&2 FAILED=$((FAILED + 1)) return 0 } local repo_ids if [[ -n $REPO ]]; then repo_ids=$REPO else repo_ids=$(jq -r '.value[] | select(.isDisabled != true) | .id' <<<"$repos") fi local repo_id for repo_id in $repo_ids; do local event for event in "${REVIEW_EVENTS[@]}"; do create_hook "$event" "$project_id" "$repo_id" done done local event for event in "${WORKITEM_EVENTS[@]}"; do create_hook "$event" "$project_id" "" done}resolve_project_id() { local project=$1 "${CURL_BASE[@]}" \ "${API}/_apis/projects/${project}?api-version=${API_VERSION}" | jq -r '.id'}if [[ -n $PROJECT ]]; then PROJECT_ID=$(resolve_project_id "$PROJECT") process_project "$PROJECT_ID"else CONTINUATION="" while true; do RESPONSE_HEADERS=$(mktemp) PROJECTS=$("${CURL_BASE[@]}" -D "$RESPONSE_HEADERS" \ "${API}/_apis/projects?\$top=200&continuationToken=${CONTINUATION}&api-version=${API_VERSION}") for project_id in $(jq -r '.value[].id' <<<"$PROJECTS"); do process_project "$project_id" done CONTINUATION=$(awk 'tolower($1) == "x-ms-continuationtoken:" {print $2}' \ "$RESPONSE_HEADERS" | tr -d '\r') rm -f "$RESPONSE_HEADERS" [[ -z $CONTINUATION ]] && break donefiecho "[SUMMARY] Created: $CREATED, replaced: $REPLACED, already existed: $EXISTING, failed: $FAILED"[[ $FAILED -gt 0 ]] && exit 1exit 0
The x-coderabbit-webhook-secret header on manually created service hooks carries the shared secret itself. Anyone who can view the service hook configuration in Azure DevOps can read it, so scope your webhook secret accordingly and rotate it from the CodeRabbit Webhook Secret page when needed.