Subscribe to Visual Studio Online events from another service

The service hooks feature is in preview. Learn more

Use the REST APIs to programmatically create subscriptions that notify your service when a specific event occurs in a team project. For example, create a subscription to notify your service when a build fails. When a user signs up for your service, you can let them choose what notifications they want and then create the subscriptions for them so that they don't have to set them up manually.

These are the events that you can subscribe to for a team project in a Visual Studio Online account:

  • build completed
  • code pushed (Git team projects)
  • code checked in (TFVC team projects)
  • work item created
  • work item updated
  • comments added to work item

You can filter these events. For example, you can filter the build completed event based on the build status.

Service hook publishers provide these events. Service hook consumers provide the actions that you can choose to take when events occur. You create a subscription that specifies the event, the consumer and the action.

Create a subscription for a team project

To create a subscription for an event, you choose which consumer to use and the action you want to take. You send an HTTP POST request to the subscriptions URL for the Visual Studio Online account with the event, consumer and action to take for the subscription.

Before you begin

You will need this data:

  • team project ID (Use this REST API to get the project id)
  • event settings (find these here including the filters for events)
  • IDs and settings for for the consumers and actions (find these here)

Authorize access

Use OAuth 2.0 to authorize access for your service to use the REST APIs for a user. Use the authorization token to get the team project id and set up the subscriptions for the team project. You will use this access token in the authorization header for your HTTP POST request.

Include this in the header for your request to use the token for authorization:

Authorization: Bearer <access_token>

Create the request

Construct the body of the HTTP POST request to create the subscription based on the team project id, event, consumer and action.

Here's an example of the JSON string for a subscription request to subscribe to an event that sends notification when the Continuous Integration build fails. It uses the Web hooks consumer to send an HTTP request containing a JSON object that represents the event to the URL in setting1 (https://myservice/event).

{
    "publisherId": "tfs",
    "eventType": "build.complete",
    "resourceVersion": "1.0-preview.1",
    "consumerId": "webHooks",
    "consumerActionId": "httpRequest",
    "publisherInputs": {
        "buildStatus": "failed",
        "definitionName": "Continuous Integration",
        "projectId": "56479caf-1eeb-4bca-86ab-aaa6f29399d9",
    },
    "consumerInputs": {
        "setting1": " https://myservice/event"
    },
}

Secure HTTPS URLs are recommended for the security of the private data in the JSON object.

This is the response:

{
    "id": "74aeeed0-bf5d-48dc-893f-f862b80987e9",
    "url": "https://{account}/DefaultCollection/_apis/hooks/subscriptions/74aeeed0-bf5d-48dc-893f-f862b80987e9",
    "status": "success",
    "publisherId": "tfs",
    "eventType": "build.complete",
    "resourceVersion": "1.0-preview.1"
    "consumerId": "webHooks",
    "consumerActionId": "httpRequest",
    "probationRetries": 2,
    "createdBy": {
        "id": "00ca946b-2fe9-4f2a-ae2f-40d5c48001bc"
    },
    "createdDate": "2014-03-28T16:10:06.523Z",
    "modifiedBy": {
        "id": "1c4978ae-7cc9-4efa-8649-5547304a8438"
    },
    "modifiedDate": "2014-04-25T18:15:26.053Z",
    "publisherInputs": {
        "buildStatus": "",
        "definitionName": "",
        "hostId": "17f27955-99bb-4861-9550-f2c669d64fc9",
        "projectId": "56479caf-1eeb-4bca-86ab-aaa6f29399d9",
        "tfsSubscriptionId": "29cde8b4-f37e-4ef9-a6d4-d57d526d82cc"
    },
    "consumerInputs": {
        "url": "http://myservice/event"
    }
}

If the subscription request fails, an HTTP response code of 400 will be returned with a message that has further details.

What happens when the event occurs?

When the event occurs in Visual Studio Online that has a subscription, the consumer action in the subscription will be performed. View the data that will be returned as a JSON string for each event here.

Your service needs to handle the action and performs any necessary tasks based on the data sent.

Resource versions

The event provider specifies the version of the resource that raised the event as resourceVersion. When you subscribe to the event, you can specify the version of the resource that you want to use in your subscription, also using resourceVersion. The resource version is the same as the API version. Within a specific API version, there may be multiple versions of the resource, specified by adding .{resource-version-number} to the end of the API version. For example, the work item tracking resources have 2 versions in the 1.0-preview version of the APIs: 1.0-preview.1 and 1.0.preview.2.

You don't have to be specific when you specify the version of the resource that you want. For example, if you use `"resourceVersion": "1.0-preview", then you'll get the latest resource version in the 1.0-preview version of the APIs. If you request a version of the resource that's no longer supported, you'll get a 401 response.

Q&A

Q: Are there services that I can subscribe to manually?

A: Yes. Here are the services that you can subscribe to from the administration page for a team project.

Q: Are there C# libraries that I can use to create subscriptions?

A: No, but here's a sample to help you get started.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Mvc;

namespace Microsoft.Samples.VisualStudioOnline
{
    public class ServiceHookEventController : Controller
    {

        // POST: /ServiceHookEvent/workitemcreated
        [HttpPost]
        public HttpResponseMessage WorkItemCreated(Content workItemEvent)
        {
            //Grabbing the title for the new workitem
            var value = RetrieveFieldValue("System.field", workItemEvent.Resource.Fields);

            //Acknowledge event receipt
            return new HttpResponseMessage(HttpStatusCode.OK);
        }

        /// <summary>
        /// Gets the value for a specified work item field.
        /// </summary>
        /// <param name="key">Key used to retrieve matching value</param>
        /// <param name="fields">List of fields for a work item</param>
        /// <returns></returns>
        public String RetrieveFieldValue(String key, IList<FieldInfo> fields)
        {
            if (String.IsNullOrEmpty(key))
                return String.Empty;

            var result = fields.Single(s => s.Field.RefName == key);

            if (result == null)
                return String.Empty;

            return result.Value;
        }

    }

    public class Content
    {
        public String SubscriptionId { get; set; }

        public int NotificationId { get; set; }

        public String EventType { get; set; }

        public WorkItemResource Resource { get; set; }

    }

    public class WorkItemResource
    {
        public String UpdatesUrl { get; set; }

        public IList<FieldInfo> Fields { get; set;}

        public int Id { get; set; }

        public int Rev { get; set; }

        public String Url { get; set; }

        public String WebUrl { get; set; }
    }

    public class FieldInfo
    {
        public FieldDetailedInfo Field { get; set; }

        public String Value { get; set; }

    }

    public class FieldDetailedInfo
    {
        public int Id { get; set; }

        public String Name { get; set; }

        public String RefName { get; set; }
    }
}