Skip to content

Latest commit

 

History

History
308 lines (222 loc) · 13.2 KB

work-with-urls.md

File metadata and controls

308 lines (222 loc) · 13.2 KB
ms.subservice title description ms.assetid ms.topic monikerRange ms.author author ms.date
azure-devops-ecosystem
Work with URLs in Extensions | Azure DevOps
Learn about best practices for working with URLs in Azure DevOps extensions and integrations.
1f27f05e-2c55-4873-ab4a-8c9c0947a7fe
concept-article
azure-devops
chcomley
chcomley
04/15/2025

Work with URLs in extensions and integrations

[!INCLUDE version-eq-azure-devops]

With Azure DevOps, organizational resources and APIs are accessible through either of the following URLs:

  • https://dev.azure.com/{organization} (new)
  • https://{organization}.visualstudio.com (legacy)

Users, tools, and integrations can interact with organization-level REST APIs using either URL, regardless of when the organization was created.

This article explains how you can best work with URLs in extensions, integrations, or tools. For more information, see the Azure DevOps Services REST API Reference.

Primary URL

Each organization has a designated primary URL that's in either the new form or the legacy form. The primary URL is used by Azure DevOps to construct URLs in certain scenarios. The default primary URL for an organization is determined by when the organization was created, but can be changed by an administrator:

When the organization was created Default primary URL
On or after 9/10/2018 New
Before 9/10/2018 Legacy

How the primary URL is used

The primary URL is the base URL for all URLs constructed by Azure DevOps in background jobs and other automated scenarios. See the following examples.

  • URLs provided to Azure Pipelines tasks via environment variables (like SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)
  • URLs included in service hooks event payloads (like URLs in resourceContainers)
  • URLs in email, Slack, Microsoft Teams, and similar notifications

For example, the following task snippet displays the organization URL provided to the task:

$orgUrl = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI
Write-Host $orgUrl

If this task is executed on an organization where the primary URL is in the new URL form, the output is https://dev.azure.com/{organization}. The same task executed on an organization where the primary URL is the legacy URL form outputs https://{organization}.visualstudio.com.

Therefore, it's important that Azure Pipelines tasks and services that receive events from service hooks handle both URL forms.

URLs returned in REST APIs

Regardless of an organization's primary URL, URLs returned in the response to a REST API call use the same base URL as the requesting URL. This function ensures that clients calling a REST API using a legacy URL continue to get back URLs in the same (legacy) form. For example, when the Projects REST API is called using a legacy URL, URLs in the response use the legacy form:

Request

GET https://Fabrikam.visualstudio.com/_apis/projects/MyProject

Response

{
  "id": "e4e4e4e4-ffff-aaaa-bbbb-c5c5c5c5c5c5",
  "name": "MyProject",
  "url": "https://Fabrikam.visualstudio.com/_apis/projects/MyProject"
}

Calling the same API using the new URL (https://dev.azure.com/Fabrikam/_apis/projects/MyProject) results in URLs returned in the new URL form.

Best practices

To ensure your extension, tool, or integration is resilient to changing organization URL forms and to possible future changes to the location (domain) of a REST API:

  • Assume the form of the organization URL can change over time.
  • Avoid parsing a URL to construct another URL.
  • Don't assume a particular REST API always resides on the same domain.
  • Avoid storing URLs in your service.
  • When possible, use Microsoft-provided .NET, TypeScript (web), Node.js, and Python client libraries with Azure DevOps.

How to get an organization's URL

With just the organization's name or ID, you can get its base URL by using the global Resource Areas REST API (https://dev.azure.com/_apis/resourceAreas). This API doesn't require authentication. It also provides information about the location (URL) of the organization and the base URL for REST APIs, which can live on different domains.

A resource area is a group of related REST API resources and endpoints. Each resource area has a well-known identifier. Each resource area has an organization-specific base URL that can be used to form URLs for APIs in that resource area. For example, the base URL for build REST APIs for the Fabrikam might be https://dev.azure.com/Fabrikam, but the base URL for release management REST APIs might be https://vsrm.dev.azure.com/Fabrikam.

Note

The Resource Areas REST API returns URLs for the organization based on that organization's designated primary URL.

Using the organization's name

There are a few ways to get the base URL for an organization by using its name.

Request

Replace {organizationName} with organization's name, for example Fabrikam. 79134C72-4A58-4B42-976C-04E7115F32BF is the ID for the core resource area, which is where important resources like projects are.

GET https://dev.azure.com/_apis/resourceAreas/79134C72-4A58-4B42-976C-04E7115F32BF
      ?accountName={organizationName}&api-version=5.0-preview.1

Response

{
    "id": "79134C72-4A58-4B42-976C-04E7115F32BF",
    "name": "Core",
    "locationUrl": "https://dev.azure.com/Fabrikam"
}

The locationUrl reflects the organization's base URL.

The Microsoft-provided .NET client library provides a helper class that calls the Resource Areas REST API for you and returns the base URL for an organization.

Note

The VssConnectionHelper class is available in 16.139.0-preview and later versions of the client library.

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
public async Task MyMethod()
{
    string organizationName = ...;
    VssCredentials credentials = ...;

    Uri organizationUrl = await VssConnectionHelper.GetOrganizationUrlAsync(organizationName);
    
    VssConnection connection = new VssConnection(organizationUrl, credentials);
    
    // get a client using connection.GetClient<T>() and do something
}
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
static readonly HttpClient s_client = new HttpClient();
static readonly string s_locationServiceUrl = "https://dev.azure.com";
static readonly Guid s_defaultResourceAreaId = Guid.Parse("79134C72-4A58-4B42-976C-04E7115F32BF");

public async Task<Uri> GetOrganizationUrl(string organizationName)
{
    string requestUrl = $"{s_locationServiceUrl}/_apis/resourceAreas/{s_defaultResourceAreaId}?accountName={organizationName}&api-version=5.0-preview.1";

    HttpResponseMessage response = await s_client.GetAsync(requestUrl);

    if (response.StatusCode == HttpStatusCode.OK)
    {
        var resourceArea = await response.Content.ReadAsAsync<JObject>();

        if (resourceArea != null)
        {
            return new Uri(resourceArea["locationUrl"].ToString());
        }
    }
    
    return null;
}
const request = require('request');

let getOrgUrl = function(orgName, callback) {
    let resourceAreaUrl = 'https://dev.azure.com/_apis/resourceAreas/79134C72-4A58-4B42-976C-04E7115F32BF?' + 
      'accountName=' + orgName +
      '&api-version=5.0-preview.1';
    
    request(resourceAreaUrl, { json: true }, (err, res, body) => {
        if (err) { 
            callback(err);   
        } else {
            callback(null, body.locationUrl);
        }
    });
};

getOrgUrl('fabrikam', (err, url) => {
    console.log(url);
});

Using the organization's ID

To get the URL for an organization by using its GUID identifier, use the hostId query parameter in the previous examples (instead of accountName). For example:

GET https://dev.azure.com/_apis/resourceAreas/79134C72-4A58-4B42-976C-04E7115F32BF?hostId={organizationId}&api-version=5.0-preview.1

How to get the base URL for a REST API

Starting from an organization's URL, you can use the Resource Areas REST API to look up the correct base URL for any REST API you need to call. This process ensures that your code is resilient to the location (domain) of a REST API changing in the future and avoids potentially brittle logic.

Note

If you're using the Microsoft-provided .NET, TypeScript (web), Node.js, or Python client library, URL lookup is handled for you. For example, when you construct a VssConnection and call GetClient<T> in .NET, the returned client is properly bound to the correct base URL for the REST APIs called by this client.

If you aren't using a Microsoft-provided client library:

  1. Use the following table to find the resource area ID for the REST API you need to call. The resource area name usually appears after /_apis/ in the REST API route. For example, the /_apis/release/definitions REST API belongs to the release resource area, which has an ID of efc2f575-36ef-48e9-b672-0c6fb4a48ac5.

  2. Call the organization-level Resource Areas REST API ({organizationUrl}/_apis/resourceAreas/{resourceAreaId}?api-version=5.0-preview.1) and pass the resource area ID. For example:

    GET https://dev.azure.com/Fabrikam/_apis/resourceAreas/efc2f575-36ef-48e9-b672-0c6fb4a48ac5?api-version=5.0-preview.1
  3. Use the locationUrl field from the JSON response as the base URL for calling other REST APIs for this area. In this example, the base URL for Release Management REST APIs is https://vsrm.dev.azure.com/Fabrikam.

Like the global Resource Areas REST API described earlier, no credentials are required to call the organization-level Resource Areas REST API.

Example: Pipelines task calling an Azure Pipelines releases REST API

In this example, a build task needs to call the Azure Pipelines releases REST API. The task forms the correct base URL for this REST API call by using the organization URL (provided in an environment variable) and the Resource Areas REST API.

Note

Resource area IDs are fixed and can be safely embedded in tasks and other logic.

$orgUrl = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI
$releaseManagementAreaId = "efc2f575-36ef-48e9-b672-0c6fb4a48ac5"

# Build the URL for calling the org-level Resource Areas REST API for the RM APIs
$orgResourceAreasUrl = [string]::Format("{0}/_apis/resourceAreas/{1}?api-preview=5.0-preview.1", $orgUrl, $releaseManagementAreaId)

# Do a GET on this URL (this returns an object with a "locationUrl" field)
$results = Invoke-RestMethod -Uri $orgResourceAreasUrl

# The "locationUrl" field reflects the correct base URL for RM REST API calls
$rmUrl = $results.locationUrl

# Construct the URL to the release definitions REST API
$releaseDefinitionsUrl = [string]::Format("{0}/_apis/release/definitions?api-preview=5.0-preview.1", $rmUrl)

Reference: Resource area IDs

This table shows the IDs for common resource areas. See the previous section for details on how to use this table.

Note

Resource area IDs are fixed and are consistent across all organizations in Azure DevOps Services.

Resource area ID Name
0d55247a-1c47-4462-9b1f-5e2125590ee6 account
5d6898bb-45ec-463f-95f9-54d49c71752e build
79bea8f8-c898-4965-8c51-8bbc3966faa8 collection
79134c72-4a58-4b42-976c-04e7115f32bf core
31c84e0a-3ece-48fd-a29d-100849af99ba dashboard
a0848fa1-3593-4aec-949c-694c73f4c4ce delegatedAuth
6823169a-2419-4015-b2fd-6fd6f026ca00 discussion
a85b8835-c1a1-4aac-ae97-1c3d0ba72dbd distributedtask
7bf94c77-0ce1-44e5-a0f3-263e4ebbf327 drop
6c2b0933-3600-42ae-bf8b-93d4f7e83594 extensionManagement
67349c8b-6425-42f2-97b6-0843cb037473 favorite
4e080c62-fa21-4fbc-8fef-2a10a2b38049 git
4e40f190-2e3f-4d9f-8331-c7788e833080 graph
68ddce18-2501-45f1-a17b-7931a9922690 memberEntitlementManagement
b3be7473-68ea-4a81-bfc7-9530baaa19ad NuGet
4c83cfc1-f33a-477e-a789-29d38ffca52e npm
45fb9450-a28d-476d-9b0f-fb4aedddff73 package
7ab4e64e-c4d8-4f50-ae73-5ef2e21642a5 packaging
2e0bf237-8973-4ec9-a581-9c3d679d1776 pipelines
fb13a388-40dd-4a04-b530-013a739c72ef policy
8ccfef3d-2b87-4e99-8ccb-66e343d2daa8 profile
efc2f575-36ef-48e9-b672-0c6fb4a48ac5 release
57731fdf-7d72-4678-83de-f8b31266e429 reporting
ea48a0a1-269c-42d8-b8ad-ddc8fcdcf578 search
3b95fb80-fdda-4218-b60e-1052d070ae6b test
c83eaf52-edf3-4034-ae11-17d38f25404c testresults
8aa40520-446d-40e6-89f6-9c9f9ce44c48 tfvc
970aa69f-e316-4d78-b7b0-b7137e47a22c user
5264459e-e5e0-4bd8-b118-0985e68a4ec5 wit
1d4f49f9-02b9-4e26-b826-2cdb6195f2a9 work
85f8c7b6-92fe-4ba6-8b6d-fbb67c809341 worktracking

Next step

[!div class="nextstepaction"] Make your extension or integration public