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 |
[!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.
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 |
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.
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:
GET https://Fabrikam.visualstudio.com/_apis/projects/MyProject
{
"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.
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.
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.
There are a few ways to get the base URL for an organization by using its name.
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
{
"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);
});
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
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:
-
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 therelease
resource area, which has an ID ofefc2f575-36ef-48e9-b672-0c6fb4a48ac5
. -
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
-
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 ishttps://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.
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)
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 |
[!div class="nextstepaction"] Make your extension or integration public