Web API Documentation
Overview
The Azure DevOps Work Item Handler API service provides comprehensive access to Azure DevOps work items across multiple organizations through three interfaces:
- REST API: Traditional HTTP endpoints for integration
- GraphQL API: Query language for flexible data retrieval
- Model Context Protocol (MCP): AI assistant integration tools
Note: The API service is currently single-tenant single-user. Support for multi-user and multi-tenant deployments is planned to be added soon.
Base URL
- REST API:
http://127.0.0.1:5016/api/v1 - GraphQL API:
http://127.0.0.1:5016/graphql - MCP Server: Via CLI command
azdw mcp
REST API Endpoints
Health & Status
GET /health
Returns basic health status.
{
"status": "Healthy",
"timestamp": "2025-01-18T10:30:00Z",
"version": "1.0.0"
}
GET /status
Returns detailed service status including configured organizations.
{
"status": "Running",
"configuredOrganizations": 2,
"organizations": ["Collaboration", "CloudPlatform"]
}
Organization Management
GET /organizations
List all configured organizations.
POST /organizations
Add or update a connection.
{
"name": "MyOrg",
"baseUrl": "https://dev.azure.com/MyOrg",
"authenticationMethod": "PAT",
"isCloud": true
}
POST /organizations/{orgName}/test
Test connectivity to an organization.
DELETE /organizations/{orgName}
Remove an organization configuration.
DELETE /organizations
Clear all organization connections. This endpoint removes all configured connections and returns the count of connections removed.
Response:
{
"success": true,
"message": "Cleared 3 connections",
"removedConnections": ["MyOrg", "CloudPlatform", "Collaboration"]
}
Note: This is a destructive operation. Ensure you have backups of connection configurations before using this endpoint.
Work Item Operations
POST /workitems/query
Query work items across organizations.
{
"workItemTypes": ["Epic", "Feature"],
"states": ["Active"],
"organizations": ["MyOrg"],
"maxResults": 100
}
For hierarchy, dependency, traceability, or linked-item discovery, include relationships explicitly and then control cross-connection or hyperlink expansion with the related flags:
{
"workItemIds": [12345],
"includeRelationships": true,
"followExternalRelationships": true,
"resolveHyperlinks": true,
"maxResults": 50
}
GET /workitems/{id}?organization={orgName}
Get a specific work item by ID.
POST /workitems
Create a new work item.
{
"organization": "MyOrg",
"project": "MyProject",
"type": "Feature",
"title": "New Feature",
"description": "Feature description",
"assignedTo": "user@company.com",
"tags": ["priority-high", "q4"]
}
PUT /workitems/{id}
Update an existing work item.
{
"organization": "MyOrg",
"title": "Updated Title",
"state": "Active",
"tags": ["updated", "reviewed"]
}
WIQL Queries
POST /wiql
Execute WIQL (Work Item Query Language) queries.
{
"query": "SELECT [System.Id], [System.Title] FROM workitems WHERE [System.WorkItemType] = 'Epic'",
"organizations": ["MyOrg"],
"maxResults": 200
}
Relationship Management
POST /relationships
Resolve work item relationships (existing endpoint).
GET /workitems/{id}/relationships?organization={orgName}
Get relationships for a specific work item.
POST /workitems/{sourceId}/relationships
Add a relationship between work items.
{
"sourceOrganization": "MyOrg",
"targetId": 67890,
"relationType": "Child",
"targetOrganization": "OtherOrg"
}
This endpoint now performs policy validation before creating relationships. If policy enforcement plugins are loaded, they will be invoked to check the proposed relationship. If any policy violations are detected, the endpoint returns 400 Bad Request with details:
{
"success": false,
"violations": [
{
"policyName": "HierarchyValidator",
"message": "Cannot link Epic to Bug - invalid hierarchy",
"severity": "Error"
}
]
}
DELETE /workitems/{sourceId}/relationships/{targetId}
Remove a relationship between work items.
Template Management
GET /templates
List all available templates.
GET /templates/{templateId}
Get a specific template.
POST /templates
Create a new template.
PUT /templates/{templateId}
Update an existing template.
DELETE /templates/{templateId}
Delete a template.
Report Generation
POST /reports/generate
Generate a report using a template.
{
"templateId": "epic-summary",
"data": { "workItems": [...] },
"parameters": { "includeDetails": true },
"renderer": "optional-plugin-renderer-name"
}
The renderer parameter allows specifying a custom plugin renderer. If provided, the system will check for a plugin implementing ICustomRendererPlugin with a matching name. If found, the plugin’s renderer is used instead of the template system. If no matching plugin is found, falls back to template rendering.
POST /visualizations/generate
Generate visualizations.
{
"data": { "workItems": [...] },
"format": "html",
"options": { "layout": "hierarchical" },
"renderer": "optional-plugin-renderer-name"
}
The renderer parameter allows specifying a custom plugin renderer for visualizations. If provided and a matching ICustomRendererPlugin is found, it will be used instead of the built-in GraphViz renderer.
Cache Management
GET /cache/status
Get cache status information.
POST /cache/refresh
Refresh cache for organization(s).
{
"organizations": ["MyOrg"],
"force": true
}
DELETE /cache
Clear cache.
- Query parameters:
organization,expiredOnly
Field Mapping Management
GET /organizations/{orgName}/fieldmappings
Get field mappings for an organization.
POST /organizations/{orgName}/fieldmappings
Update field mappings.
{
"Epic": {
"fields": {
"title": "System.Title",
"priority": "Microsoft.VSTS.Common.Priority"
}
}
}
POST /organizations/{orgName}/fieldmappings/generate
Generate field mappings automatically.
Pending Operations (Approval Workflow)
The pending operations endpoints implement an HTTP 202 Accepted pattern for AI-initiated operations that require user approval. This is used when the AI approach is configured to GitHubCopilotSdk or when explicit approval workflows are needed.
GET /operations
List all pending operations.
Response:
[
{
"id": "op-abc123",
"operationType": "UpdateWorkItem",
"description": "Update Bug #1234: set priority to Critical",
"status": "Pending",
"createdAt": "2024-01-15T10:30:00Z",
"expiresAt": "2024-01-15T10:45:00Z"
}
]
GET /operations/{id}
Get status of a specific operation.
Response (Pending):
{
"id": "op-abc123",
"operationType": "UpdateWorkItem",
"description": "Update Bug #1234: set priority to Critical",
"status": "Pending",
"createdAt": "2024-01-15T10:30:00Z",
"expiresAt": "2024-01-15T10:45:00Z"
}
Response (Completed):
{
"id": "op-abc123",
"operationType": "UpdateWorkItem",
"description": "Update Bug #1234: set priority to Critical",
"status": "Approved",
"result": { "workItemId": 1234, "success": true }
}
POST /operations/{id}/approve
Approve a pending operation. The operation will be executed and the result returned.
Response:
{
"id": "op-abc123",
"status": "Approved",
"result": { "workItemId": 1234, "success": true }
}
POST /operations/{id}/reject
Reject a pending operation.
Request (optional):
{
"reason": "Operation not authorized by team lead"
}
Response:
{
"id": "op-abc123",
"status": "Rejected"
}
Operation Status Values:
Pending- Awaiting user approvalApproved- Approved and executed successfullyRejected- Rejected by userExpired- Approval timeout exceeded (default: 15 minutes)Failed- Execution failed after approval
GraphQL API
Queries
Available Queries:
getHealth: Simple health checkgetOrganizations: List configured organizationsgetOrganization(name: String!): Get specific organizationqueryWorkItems(filter: QueryFilterInput!): Query work itemsgetWorkItem(id: Int!, organizationName: String!): Get specific work itemexecuteWiql(query: String!, organizationNames: [String], maxResults: Int): Execute WIQLgetWorkItemRelationships(workItemId: Int!, organizationName: String!, includeExternal: Boolean): Get relationshipsgetTemplates(category: String, outputFormat: String): Get templates
Example Query:
query {
queryWorkItems(filter: {
workItemTypes: ["Epic", "Feature"]
states: ["Active"]
maxResults: 50
}) {
workItems {
id
title
type
state
organization
assignedTo
}
totalCount
}
}
For hierarchy, dependency, traceability, or linked-item discovery, set includeRelationships: true. Hyperlink and cross-connection expansion stay explicit query choices rather than being implied automatically.
Mutations
Available Mutations:
addOrganization(input: ConnectionInput!): Add organizationremoveOrganization(name: String!): Remove organizationtestConnection(name: String!): Test connectioncreateTemplate(input: RenderingTemplateInput!): Create templateupdateTemplate(input: RenderingTemplateInput!): Update templatedeleteTemplate(templateId: String!): Delete templatecreateWorkItem(input: CreateWorkItemInput!): Create work itemupdateWorkItem(input: UpdateWorkItemInput!): Update work itemaddRelationship(input: AddRelationshipInput!): Add relationshipremoveRelationship(input: RemoveRelationshipInput!): Remove relationshipgenerateReport(input: GenerateReportInput!): Generate reportclearCache(input: ClearCacheInput!): Clear cacherefreshCache(input: RefreshCacheInput!): Refresh cache
Example Mutation:
mutation {
createWorkItem(input: {
organization: "MyOrg"
project: "MyProject"
type: "Feature"
title: "New Feature"
description: "Feature description"
}) {
success
workItem {
id
title
type
}
errors
}
}
Model Context Protocol (MCP)
The MCP server exposes Azure DevOps functionality as tools for AI assistants.
Starting the MCP Server
azdw mcp
Bridging MCP to OpenAPI
If you need a local OpenAPI server without hosting the full API Service, you can bridge the CLI MCP server to OpenAPI using mcpo.
mcpo (Model Context Protocol to OpenAPI) is a bridge tool that converts MCP servers into OpenAPI-compatible endpoints. This allows you to:
- Use the lightweight CLI MCP server instead of the full API service
- Integrate with tools that expect OpenAPI/REST interfaces
- Access azdw functionality through standard HTTP requests
Example usage:
# Bridge the azdw MCP server to OpenAPI using mcpo
# mcpo will start the MCP server automatically
mcpo --port 8000 -- azdw mcp
The MCP server will be accessible at http://localhost:8000 with auto-generated OpenAPI documentation at http://localhost:8000/docs.
This approach is ideal for scenarios where you want OpenAPI compatibility without the overhead of running the full ASP.NET Core API service.
Available Tools
query_work_items
Query work items with flexible filtering.
- Parameters: organizations, workItemTypes, states, assignedTo, titleContains, maxResults, includeRelationships
- For hierarchy, dependency, traceability, and linked-item requests,
includeRelationshipsis often needed even though it is not the default.
execute_wiql
Execute WIQL queries.
- Parameters: query, organizations, maxResults
list_organizations
List all configured organizations.
add_connection
Add a new Azure DevOps connection.
- Parameters: name, baseUrl, authMethod, scope, projectName
remove_connection
Remove a specific Azure DevOps connection.
- Parameters: name
clear_all_connections
Clear all configured connections. This is a destructive operation that removes all organization connections. The tool uses a two-phase approval workflow with Critical risk level, showing a preview of connections to be removed before execution.
test_connection
Test connectivity to a specific connection.
- Parameters: name
list_templates
List available rendering templates.
- Parameters: category, outputFormat
get_work_item
Get a specific work item by ID.
- Parameters: id, organization, includeRelationships, includeHistory
create_work_item
Create a new work item, including custom Azure DevOps fields.
- Parameters: connection, project, type, title, description, assignedTo, tags, customFields, comment
customFieldsis a JSON object whose keys are Azure DevOps field reference names such asCustom.MyFieldorMicrosoft.VSTS.Common.Priority.
update_work_item
Update an existing work item, including custom Azure DevOps fields.
- Parameters: id, connection, title, description, state, assignedTo, tags, customFields, comment
customFieldsis a JSON object whose keys are Azure DevOps field reference names. Set a value tonullto remove that field.
get_work_item_relationships
Get work item relationships.
- Parameters: id, organization, includeExternal, depth
add_work_item_relationship
Add a relationship between work items.
- Parameters: sourceId, targetId, relationType, sourceOrganization, targetOrganization
This tool now performs policy validation during the approval process. If policy enforcement plugins are loaded, they will check the proposed relationship. Policy violations are included in the approval details and elevate the risk level to High, requiring explicit user confirmation before proceeding.
remove_work_item_relationship
Remove a relationship between work items.
- Parameters: sourceId, targetId, relationType, sourceOrganization, targetOrganization
generate_report
Generate reports using templates.
- Parameters: templateId, dataJson, parametersJson, rendererName (optional), format (optional)
Added rendererName and format parameters for custom plugin renderer support. If rendererName is provided, the system will attempt to use a matching ICustomRendererPlugin instead of template rendering.
get_current_date_time
Get the current date and time in the MCP server’s local timezone (or a specified timezone).
- Parameters: format (optional, default: ‘iso8601’), timeZoneId (optional, default: local system timezone)
- Returns: Current datetime with timezone information including UTC offset and daylight saving time status
- Formats: ‘iso8601’, ‘rfc1123’, ‘short’, ‘long’, ‘date’, ‘time’, or custom .NET format string
- Timezone: Accepts IANA (e.g., ‘America/New_York’) or Windows (e.g., ‘Eastern Standard Time’) timezone IDs
get_cache_status
Get cache status information.
- Parameters: organization
clear_cache
Clear cache for organization(s).
- Parameters: organization, expiredOnly
refresh_cache
Refresh cache for organization(s).
- Parameters: organizations, force
Feature Implementation Status
✅ Fully Implemented
- Work item querying (query_work_items, queryWorkItems)
- WIQL query execution (execute_wiql, executeWiql)
- Organization management (list, add, remove, test)
- Template management (CRUD operations)
- Basic relationship resolution
🚧 Partially Implemented
- Individual work item operations (endpoints exist, need API client methods)
- Report generation (template framework exists, needs renderer fixes)
- Cache management (interfaces defined, need implementation)
- Field mapping operations (service exists, needs API exposure)
❌ Not Yet Implemented
- Work item creation/update (API client methods needed)
- Relationship add/remove (API client methods needed)
- Advanced visualization generation
- Cache service implementation
Error Handling
All APIs return consistent error responses:
REST API Errors
{
"error": "Error message",
"details": "Additional error details"
}
GraphQL Errors
{
"data": null,
"errors": [
{
"message": "Error message",
"path": ["fieldName"]
}
]
}
MCP Tool Errors
{
"success": false,
"error": "Error message"
}
Pagination
Both REST and GraphQL APIs support pagination for endpoints that return collections, ensuring efficient handling of large datasets.
REST API Pagination
REST endpoints use offset-based pagination with page numbers and page sizes.
Pagination Parameters:
page: Page number (default: 1, minimum: 1)pageSize: Items per page (default: 100, maximum: 1000)
Pagination Response Structure: All paginated REST endpoints return responses with the following structure:
{
"data": [...],
"pagination": {
"page": 1,
"pageSize": 100,
"totalItems": 250,
"totalPages": 3,
"hasNextPage": true,
"hasPreviousPage": false,
"links": {
"self": "/api/v1/endpoint?page=1&pageSize=100",
"first": "/api/v1/endpoint?page=1&pageSize=100",
"last": "/api/v1/endpoint?page=3&pageSize=100",
"next": "/api/v1/endpoint?page=2&pageSize=100",
"previous": null
}
}
}
Paginated REST Endpoints:
POST /workitems/query- Query work itemsPOST /wiql- Execute WIQL queriesPOST /relationships- Resolve relationshipsGET /templates- List templatesGET /relationships/orphans- Find orphaned work itemsGET /relationships/circular- Find circular dependenciesGET /plugins- List plugins
Example:
# Get second page with 50 items per page
curl "http://localhost:5000/api/v1/workitems/query?page=2&pageSize=50" \
-H "Content-Type: application/json" \
-d '{"connections":["MyOrg"],"workItemTypes":["Epic"]}'
HATEOAS Links: The links object provides REST-standard hypermedia navigation:
self: Current page URLfirst: First page URLlast: Last page URLnext: Next page URL (null if on last page)previous: Previous page URL (null if on first page)
GraphQL Pagination
GraphQL endpoints use cursor-based pagination following the Relay specification.
Pagination Arguments:
first: Number of items to fetch (forward pagination)after: Cursor to fetch items after (forward pagination)last: Number of items to fetch (backward pagination)before: Cursor to fetch items before (backward pagination)
Default: first: 100, Maximum: first/last: 1000
Connection Response Structure: Paginated GraphQL queries return a Connection type with:
{
edges {
node {
# Your data fields
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
Paginated GraphQL Queries:
getConnections- List configured connectionsgetTemplates- List templatesfindOrphanedWorkItems- Find orphaned work itemsfindCircularDependencies- Find circular dependenciesworkItemTypes- List work item typesworkItemFields- List work item fields
Example - Forward Pagination:
query {
getTemplates(first: 10) {
edges {
node {
id
name
category
outputFormat
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
Example - Fetching Next Page:
query {
getTemplates(first: 10, after: "cursor_value_from_previous_page") {
edges {
node {
id
name
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
Example - Backward Pagination:
query {
findOrphanedWorkItems(
connections: ["MyOrg"]
last: 20
before: "cursor_value"
) {
edges {
node {
id
title
type
}
cursor
}
pageInfo {
hasPreviousPage
startCursor
}
}
}
Cursor-Based vs Offset-Based:
- Cursors are opaque tokens that represent a position in the dataset
- More efficient for large datasets and real-time data
- Prevents “page drift” issues when data is added/removed during pagination
- Required for GraphQL Relay specification compliance
Authentication
The API service uses the same authentication as the CLI:
- Personal Access Tokens (PAT)
- Device Code Flow
- Interactive Browser Flow
Credentials are managed through the CLI and shared with the API service.
Rate Limiting
The service respects Azure DevOps rate limits and includes rate limiting information in responses where applicable.
Next Steps for Full Implementation
- API Client Enhancement: Implement missing methods in AzureDevOpsApiClient
- CreateWorkItemAsync
- UpdateWorkItemAsync
- AddWorkItemRelationshipAsync
- RemoveWorkItemRelationshipAsync
- Cache Service: Complete ICacheService implementation
- GetCacheStatus
- RefreshCacheAsync
- ClearCacheAsync
- Rendering Fixes: Fix renderer method signatures and dependencies
- GraphVizRenderer.RenderAsync
- InteractiveHtmlRenderer parameter types
- Field Mapping API: Add missing IFieldMappingService methods
- GetFieldMappingsForOrganization
- UpdateFieldMappingsAsync
- GenerateFieldMappingsAsync
- Testing: Comprehensive API testing across all interfaces
This documentation will be updated as features are fully implemented.
Hosted Admin Endpoints
These endpoints are only available in hosted/cloud deployments where IHostedAuditService is registered.
Audit Trail
Authorization: AdminRequired policy (scope=admin claim required).
Query by Actor
GET /api/v1/admin/audit?actor={userScopeId}&maxResults={n}
Returns all audit records for the specified user scope ID, most recent first.
| Parameter | Type | Required | Description |
|---|---|---|---|
actor | string | Yes | User scope ID to query |
maxResults | int | No | Maximum records to return (default: 100) |
Responses: 200 OK (array of AuditRecord), 400 Bad Request (missing actor), 401/403 (unauthorized), 404 Not Found (non-hosted mode).
Query by Capability
GET /api/v1/admin/audit/capability/{capabilityId}?maxResults={n}
Returns all audit records for the specified capability ID (e.g. "chat", "web-ui-settings", "approve", "reject").
| Parameter | Type | Required | Description |
|---|---|---|---|
capabilityId | string (path) | Yes | Capability identifier |
maxResults | int | No | Maximum records to return (default: 100) |
Responses: 200 OK (array of AuditRecord), 401/403 (unauthorized), 404 Not Found (non-hosted mode).
AuditRecord Schema
{
"auditId": "string",
"actorUserScopeId": "string",
"capabilityId": "string",
"outcome": "approved | rejected | expired | overwritten | ...",
"reasonClass": "string",
"contextNote": "string | null",
"relatedOperationId": "string | null",
"recordedUtc": "2025-01-01T00:00:00Z",
"retainUntilUtc": "2026-01-01T00:00:00Z"
}