Azure DevOps

A first-class Azure Boards adapter with Entra and PAT authentication, WIQL discovery, custom-process normalization, Markdown comments, priority mapping, hierarchy links, and native revision checks.

Setup

azure-work.ts
import { createWorkClient } from "work-sdk";
import { azureDevOps } from "work-sdk/azure-devops";

const work = createWorkClient({
  adapter: azureDevOps({
    organization: "acme",
    project: "Platform",
    auth: { type: "entra", token: accessToken },
  }),
});

The adapter targets Azure DevOps REST API 7.1. The organization and project are path-scoped, so an accidentally supplied foreign project is rejected rather than silently redirected.

Authentication

Microsoft Entra ID

Use Entra access tokens for production applications, service principals, or managed identities.

auth: { type: "entra", token: accessToken }

Personal access token

PATs are convenient for local scripts and prototypes. They are sent as Basic credentials with an empty username.

auth: { type: "pat", token: process.env.AZURE_DEVOPS_PAT! }

PATs are long-lived credentials. Prefer Entra tokens in production, scope credentials to work-item access, rotate them, and keep them out of browser code and agent context.

Custom processes

Azure organizations can rename states and introduce custom work-item types. Reads must map provider names to the portable model; creates need the reverse mapping. Work SDK keeps both directions explicit.

custom-process.ts
const adapter = azureDevOps({
  organization: "acme",
  project: "Platform",
  auth: { type: "entra", token: accessToken },

  // Provider state name → normalized read/filter state
  stateMap: {
    "Ready for validation": "started",
    "Released": "completed",
  },

  // Provider type name → normalized read kind
  workItemTypeMap: {
    "Customer Request": "story",
  },

  // Normalized create kind → tenant-specific provider type
  workItemTypeByKind: {
    story: "Product Backlog Item",
  },

  defaultWorkItemType: "Task",
});

Built-in defaults cover common Agile, Scrum, and Basic process names. Custom entries extend or override those case-insensitive defaults.

Read, search, and write

azure-operations.ts
const page = await work.list({
  query: "retry webhook",
  state: "started",
  labels: ["reliability"],
  limit: 50,
});

const change = await work.prepareUpdate("42", {
  state: "Ready for validation", // exact Azure state name
  priority: "high",              // maps to Azure priority 2
  assigneeIds: ["ada@example.com"],
  labels: ["sdk", "agent-safe"],
  parentId: "10",
});

await work.commit(change, {
  idempotencyKey: "release:42:validation",
});

Write state values are provider-native on purpose. Work SDK does not guess whether normalized completed means Done, Closed, Released, or another custom state.

Field mapping

Work SDKAzure DevOpsNotes
titleSystem.TitleRequired on create
descriptionSystem.DescriptionReturned as provider HTML
stateNameSystem.StateExact provider state
priorityMicrosoft.VSTS.Common.Priorityurgent/high/medium/low → 1/2/3/4
assigneeIds[0]System.AssignedToIdentity ID, email, or resolvable name
labelsSystem.TagsSemicolon-separated provider field
parentIdHierarchy-Reverse relationExisting parent replaced atomically
revisionrevOpaque string to consumers

WIQL search and pagination

list builds an escaped WIQL query, fetches ordered IDs, and hydrates pages through the batch work-item endpoint. Cursors are opaque and scoped to this adapter.

  • Maximum page size: 100
  • WIQL window: 20,000 results
  • Batch hydration: at most 100 items per SDK page
  • State filters use the configured state-name map

WIQL offset pagination is not a snapshot. For long-running crawls, items changing between pages can move in the order. Use targeted filters and restart a crawl when strict snapshot semantics matter.

Native revision protection

Prepared updates capture Azure's numeric rev. Commit first rechecks the normalized revision, then sends a JSON Patch test operation against /rev in the same mutation request. A stale write fails as WorkConflictError.

Azure DevOps Server and proxies

apiBaseUrl replaces https://dev.azure.com/{organization}. Point it at the collection root; the adapter appends the encoded project and API paths.

azureDevOps({
  organization: "collection-label",
  project: "Platform",
  apiBaseUrl: "https://devops.example.com/tfs/DefaultCollection",
  auth: { type: "pat", token },
})

Known boundaries

  • Boards, iterations, area paths, attachments, relations other than parent, and custom fields are outside the portable v0.x model.
  • Descriptions are preserved as provider HTML; Work SDK does not perform lossy HTML-to-Markdown conversion.
  • Identity writes depend on Azure DevOps resolving the supplied ID, email, or name.
  • Provider rules can modify fields. The adapter verifies important returned fields and fails closed on mismatches.
NextClient API referenceReview every normalized read, prepare, and commit method.