C#: Creating Work Items in Azure DevOps using REST API

Last updated on 29th April 2020

Azure DevOps Services, formerly known as Visual Studio Team Services(VSTS) is a Microsoft product that provides DevOps capabilities for application development such as automation, collaboration, requirements management, test management, Continuous integration (CI), Continuous Delivery (CD) and monitoring. Azure DevOps Services REST API enables you to interact with Azure DevOps programmatically. You can make REST API calls to create, list, update and delete various resources in Azure DevOps links projects, boards, work items, test cases, commits, files, so on, and so forth. In this tutorial, you will learn how to how to make REST API calls from a C# console application to create work items in an Azure DevOps project.

Create C# Project in Visual Studio

First of all lets create a C# console app in Visual Studio. To do this,

  • Open Visual Studio and click File → New Project.
  • Choose Visual C# from the Language list and select Console Application.
  • Enter CreatWorkItems for name and click OK.
  • Visual Studio will create a new Project, a Solution and a class named Program. The Program class contains the Main() method which is the entry point for your console app. Click File → Save All to save your application.

Install Json.NET Package

The data that is exchanged between your application and Azure DevOps REST API service is in JSON format. So the next step is to install Json.Net which is a JSON framework for .NET that allows you to serialize and deserialize the data easily.

  • Click Tools → Nuget Package Manager → Manage Nuget Packages for Solution.
  • Click the Browse tab and search for Newtonsoft
  • Select Newtonsoft.Json from the search results. Select your project and click Install. Click OK to proceed if prompted.

    Nuget Package Manager

Create a Personal Access Token

A Personal Access Token (PAT) is required to authenticate your application with Azure DevOps Services. Click here to learn how to generate a PAT.

Program flow

  1. Azure Class: Inside a static class named Azure we have defined the following constants that are specific to your Azure DevOps environment. Make sure you update these values for your environment.

    • BASE: This is the URL for Azure DevOps services and for Azure cloud it is https://dev.azure.com/
    • PAT: This is the Personal Access Token(PAT) that you generated earlier.
    • ORG: - Name of your organization in Azure DevOps.
    • API - REST API version string. The endpoints (URIs) used in the program are from API version 5.1-preview.
    • PROJECT: Name of any project in Azure DevOps. We will create and manage the work items in this project.
    • WIT_TYPE: Work Item Type. In this example, we are creating a Task but you can create any work item, for example, Bug, Issue etc.,
  2. Main Method: Main() method is where the program execution starts and the following actions are performed inside the Main method.

    • Create and initialize a new instance of HttpClient class. The HttpClient class is used for sending HTTP requests and receive responses from the REST API endpoints (URIs).
    • Set response media type to "application/json" in Request header.
    • Generate a Base 64 encoded string from PAT and sets a Basic Authorization in Header.
    • Create Request body in JSON format
    • Builds the REST API URI for creating a Work Item by joining various parameters. The endpoint URI for creating a Work Item is as below:

      POST https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/${type}?api-version=5.1
    • Call CreateWIT() with the HttpClient object, URI and Request body as parameters and wait for the result.
    • And finally, pretty print the JSON result.
  3. CreateWIT Method: The (HttpClient client, string uri, HttpContent content) method sends an asynchronous POST request on the resource URI which is passed to this method as a parameter. If the POST is successful, this method returns the JSON string response received from REST API service otherwise it prints the error message and returns an empty string.

Program source code

Here is the full code.

using System;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;

namespace CreatWorkItems
{
  static class Azure
    {
      public const string BASE = "https://dev.azure.com";
      public const string PAT = "3ljqhiwlkowddiqscp2xd6nd3rwfs2g6vs5yfaeg2kcdzsgi264a";
      public const string ORG = "OpenTechGuides";
      public const string API = "api-version=5.1";
      public const string PROJECT = "OTGRESTDemo";
      public const string WIT_TYPE = "$Task";
    }
    
 class Program
    {
        static void Main(string[] args)
        {
           // Create and initialize HttpClient instance.
           HttpClient client = new HttpClient();

           // Set Media Type of Response.
           client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

           // Generate base64 encoded authorization header.
           client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", "", Azure.PAT))));

           // Build the URI for creating Work Item.
           string uri = String.Join("?", String.Join("/", Azure.BASE, Azure.ORG, Azure.PROJECT, "_apis/wit/workitems", Azure.WIT_TYPE), Azure.API);
    
           // Create Request body in JSON format.
           string json = "[{ \"op\": \"add\", \"path\": \"/fields/System.Title\", \"from\": null, \"value\": \"REST API Demo task\"}]";
           HttpContent content = new  StringContent(json, Encoding.UTF8, "application/json-patch+json");
           
           // Call CreateWIT method.
            string result = CreateWIT(client, uri, content).Result;

            // Pretty print the JSON if result not empty or null.
            if (!String.IsNullOrEmpty(result))
            {
                dynamic wit = JsonConvert.DeserializeObject<object>(result);
                Console.WriteLine(JsonConvert.SerializeObject(wit, Formatting.Indented));
            }
     
            // Presss any key to exit
            Console.ReadLine();
            client.dispose();
        }
 
        public static async Task<string> CreateWIT(HttpClient client, string uri, HttpContent content)
        {
            try
            {
                // Send asynchronous POST request.
                using (HttpResponseMessage response = await client.PostAsync(uri, content))
                {
                    response.EnsureSuccessStatusCode();
                    return (await response.Content.ReadAsStringAsync());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return string.Empty;
            }
        } // End of CreateWIT method
 }
}

Common Errors

Here are some of the common errors that you encounter while working with REST API and their potential causes and resolution.

  • 404 Page Not Found : If you get a HTTP error code 404: Page Not Found as response then the most likely the URI to which you are making the request may be incorrect. Please check the URI and correct it. Note that in the URI for creating work items, the Work Item Type must be preceded by a $ sign.
  • 400 Bad Request : If you get a HTTP error code 400: Bad Request, then either you have some error in the payload (Request body). Check the JSON to ensure no keys or values are missing. Also verify that the content type is set to application/json-patch+json.

Post a comment

Comments

cen | May 5, 2023 7:24 AM |

please tell me why you using WIT_TYPE = "$Task" not ${type}? Thank?

Navya | August 5, 2021 8:40 PM |

How to add data into a custom field in Classification?

Albert | September 24, 2020 3:27 PM |

thank you for the article, well explained!! I would like to ask if it is posible to add a text in the description field??

Jon B | October 1, 2020 9:43 AM |

In the content JSON, add the field System.Description

Kalpana | August 19, 2020 11:31 PM |

Thank you very much. It was well explained. Can you show the same thing using ASP.Net/MVC simillar shown for console application.

karthik | July 8, 2020 4:34 PM |

Thank you very much for the post, it help me a lot to automate my workflow in few minutes