> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vortexiq.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Azure DevOps on Vortex IQ

> Monitor Azure DevOps health, cost and reliability signals, and catch incidents and runaway spend early.

export const CapabilityPage = ({data}) => {
  const d = data || ({});
  const [tab, setTab] = useState('overview');
  const [sel, setSel] = useState(null);
  const [q, setQ] = useState('');
  const [outcome, setOutcome] = useState('');
  const [status, setStatus] = useState('');
  const [limit, setLimit] = useState(8);
  const monitor = d.monitor || [];
  const audits = d.audits || [];
  const autos = d.autos || [];
  const outcomes = useMemo(() => {
    const s = new Set();
    monitor.forEach(m => s.add(m.o));
    audits.forEach(a => s.add(a.o));
    return [...s].sort();
  }, [monitor, audits]);
  const rows = useMemo(() => {
    const t = q.trim().toLowerCase();
    if (tab === 'monitor') {
      return monitor.filter(m => (!t || (m.n + ' ' + (m.d || '')).toLowerCase().includes(t)) && (!outcome || m.o === outcome));
    }
    if (tab === 'audit') {
      return audits.filter(a => (!t || (a.n + ' ' + (a.why || '')).toLowerCase().includes(t)) && (!outcome || a.o === outcome) && (!status || a.s === status));
    }
    if (tab === 'automate') {
      return autos.filter(w => !t || (w.n + ' ' + (w.d || '')).toLowerCase().includes(t));
    }
    return [];
  }, [tab, q, outcome, status, monitor, audits, autos]);
  const pill = (text, kind) => {
    const tone = kind === 'good' ? {
      color: '#1f9e54',
      borderColor: '#1f9e54',
      background: '#ecf8f110'
    } : kind === 'warn' ? {
      color: '#c07a17',
      borderColor: '#c07a17',
      background: '#fdf6e910'
    } : kind === 'bad' ? {
      color: '#c53527',
      borderColor: '#c53527',
      background: '#fdf0ee10'
    } : kind === 'brand' ? {
      color: '#5529d6',
      borderColor: '#5529d6'
    } : {
      color: 'inherit',
      borderColor: 'currentColor',
      opacity: 0.65
    };
    return <span key={text} style={{
      fontSize: '10.5px',
      fontFamily: 'ui-monospace, monospace',
      border: '1px solid',
      borderRadius: '999px',
      padding: '2px 8px',
      whiteSpace: 'nowrap',
      ...tone
    }}>
        {text}
      </span>;
  };
  const sevKind = s => s === 'critical' ? 'bad' : s === 'high' ? 'warn' : 'muted';
  const fixKind = s => s === 'Prepared fix' ? 'good' : s === 'Candidate remediation' ? 'warn' : 'muted';
  const card = (item, kind) => {
    const title = item.n;
    const badges = kind === 'monitor' ? [pill(item.o, 'brand'), pill(item.a === 'Watch only' || item.a === 'Merchant rule' ? item.a : `Alert ${item.a}`, 'muted')] : kind === 'audit' ? [pill(item.sev, sevKind(item.sev)), pill(item.s, fixKind(item.s))] : [pill(item.role, 'brand'), pill(item.ready, 'good')];
    const body = kind === 'monitor' ? item.nc ? 'Description pending editorial review; the signal is live.' : item.d : kind === 'audit' ? item.why : item.d;
    return <button key={title} onClick={() => setSel({
      kind,
      item
    })} style={{
      display: 'block',
      width: '100%',
      textAlign: 'left',
      border: '1px solid rgba(128,128,128,.28)',
      borderRadius: '10px',
      padding: '12px 14px',
      marginBottom: '8px',
      background: 'transparent',
      cursor: 'pointer',
      font: 'inherit',
      color: 'inherit'
    }}>
        <div style={{
      display: 'flex',
      gap: '10px',
      justifyContent: 'space-between',
      alignItems: 'flex-start',
      flexWrap: 'wrap'
    }}>
          <strong style={{
      fontSize: '14px'
    }}>{title}</strong>
          <span style={{
      display: 'flex',
      gap: '5px',
      flexWrap: 'wrap'
    }}>{badges}</span>
        </div>
        {body ? <div style={{
      fontSize: '12.5px',
      opacity: 0.72,
      marginTop: '4px'
    }}>{body}</div> : null}
      </button>;
  };
  const tabBtn = (id, label) => <button key={id} onClick={() => {
    setTab(id);
    setLimit(8);
    setQ('');
  }} style={{
    border: 0,
    background: 'none',
    font: 'inherit',
    fontWeight: 600,
    fontSize: '14px',
    padding: '9px 14px',
    cursor: 'pointer',
    color: tab === id ? '#5529d6' : 'inherit',
    opacity: tab === id ? 1 : 0.65,
    borderBottom: tab === id ? '2px solid #5529d6' : '2px solid transparent'
  }}>
      {label}
    </button>;
  const inputStyle = {
    border: '1px solid rgba(128,128,128,.3)',
    borderRadius: '999px',
    padding: '7px 13px',
    font: 'inherit',
    fontSize: '13px',
    background: 'transparent',
    color: 'inherit'
  };
  return <div style={{
    position: 'relative'
  }}>
      {}
      <div style={{
    display: 'grid',
    gridTemplateColumns: 'repeat(auto-fit,minmax(120px,1fr))',
    gap: '10px',
    margin: '18px 0'
  }}>
        {(d.stats || []).map(([v, l]) => <div key={l} style={{
    border: '1px solid rgba(128,128,128,.28)',
    borderRadius: '10px',
    padding: '11px 13px'
  }}>
            <div style={{
    fontSize: v.length > 6 ? '15px' : '21px',
    fontWeight: 700,
    letterSpacing: '-.02em',
    color: v.length > 6 ? '#5529d6' : 'inherit'
  }}>{v}</div>
            <div style={{
    fontSize: '11px',
    opacity: 0.66
  }}>{l}</div>
          </div>)}
      </div>

      <div style={{
    display: 'flex',
    gap: '4px',
    borderBottom: '1px solid rgba(128,128,128,.25)',
    marginBottom: '16px',
    flexWrap: 'wrap'
  }}>
        {tabBtn('overview', 'Overview')}
        {tabBtn('monitor', `Monitor (${monitor.length})`)}
        {tabBtn('audit', `Audit (${audits.length})`)}
        {tabBtn('automate', 'Automate')}
      </div>

      {tab === 'overview' && <div>
          <div style={{
    display: 'grid',
    gridTemplateColumns: 'repeat(auto-fit,minmax(240px,1fr))',
    gap: '10px'
  }}>
            {outcomes.map(o => <div key={o} style={{
    border: '1px solid rgba(128,128,128,.28)',
    borderLeft: '3px solid #5529d6',
    borderRadius: '10px',
    padding: '11px 14px'
  }}>
                <strong style={{
    fontSize: '13.5px'
  }}>{o}</strong>
              </div>)}
          </div>
          <p style={{
    fontSize: '13px',
    opacity: 0.75,
    marginTop: '16px'
  }}>
            Every capability follows the same controlled sequence: connect, monitor, detect, recommend,
            approve, execute, verify. Nothing changes a connected system without the approval step.
          </p>
        </div>}

      {tab !== 'overview' && <div>
          <div style={{
    display: 'flex',
    gap: '8px',
    marginBottom: '12px',
    flexWrap: 'wrap'
  }}>
            <input style={{
    ...inputStyle,
    flex: 1,
    minWidth: '170px'
  }} placeholder={`Search ${tab === 'monitor' ? 'signals' : tab === 'audit' ? 'checks' : 'workflows'}...`} value={q} onChange={e => {
    setQ(e.target.value);
    setLimit(8);
  }} />
            {tab !== 'automate' && <select style={inputStyle} value={outcome} onChange={e => {
    setOutcome(e.target.value);
    setLimit(8);
  }}>
                <option value="">All outcomes</option>
                {outcomes.map(o => <option key={o} value={o}>{o}</option>)}
              </select>}
            {tab === 'audit' && <select style={inputStyle} value={status} onChange={e => {
    setStatus(e.target.value);
    setLimit(8);
  }}>
                <option value="">All fix statuses</option>
                <option>Prepared fix</option>
                <option>Candidate remediation</option>
                <option>Report only</option>
              </select>}
          </div>

          {rows.length === 0 && tab === 'automate' && <div style={{
    border: '1px dashed #5529d6',
    borderRadius: '10px',
    padding: '15px'
  }}>
              <strong style={{
    fontSize: '14px'
  }}>Ready to build your first {d.name} workflow</strong>
              <p style={{
    fontSize: '12.5px',
    opacity: 0.75,
    margin: '6px 0 0'
  }}>
                Vortex IQ is integrated with {d.reads} read and {d.writes} write operations on {d.name}.
                Pick a trigger, add them as steps, and every step that changes data pauses for approval.
              </p>
            </div>}
          {rows.length === 0 && tab !== 'automate' && <div style={{
    opacity: 0.6,
    fontSize: '13px'
  }}>Nothing matches this search or filter.</div>}

          {rows.slice(0, limit).map(r => card(r, tab))}

          {rows.length > limit && <button onClick={() => setLimit(rows.length)} style={{
    ...inputStyle,
    display: 'block',
    margin: '10px auto 0',
    cursor: 'pointer'
  }}>
              View all {rows.length}
            </button>}
        </div>}

      {}
      {sel && <>
          <div onClick={() => setSel(null)} style={{
    position: 'fixed',
    inset: 0,
    background: 'rgba(10,6,26,.45)',
    zIndex: 40
  }} />
          <aside style={{
    position: 'fixed',
    top: 0,
    right: 0,
    width: 'min(430px, 94vw)',
    height: '100vh',
    overflowY: 'auto',
    background: 'var(--background, #fff)',
    backgroundColor: 'light-dark(#fff, #0c061f)',
    borderLeft: '1px solid rgba(128,128,128,.3)',
    padding: '20px 22px',
    zIndex: 50
  }}>
            <button onClick={() => setSel(null)} style={{
    float: 'right',
    border: '1px solid rgba(128,128,128,.3)',
    background: 'transparent',
    color: 'inherit',
    borderRadius: '999px',
    width: '30px',
    height: '30px',
    cursor: 'pointer'
  }}>
              ×
            </button>
            <DetailBody sel={sel} d={d} pill={pill} sevKind={sevKind} fixKind={fixKind} />
          </aside>
        </>}
    </div>;
};

Monitor Azure DevOps health, cost and reliability signals, and catch incidents and runaway spend early.

No changes are made without the configured approval policy. Read-only operations do not modify the connected system; schedules, access scopes, API usage and data handling remain governed by Vortex IQ controls.

[Connect or manage this source](https://app.vortexiq.ai/workbench/settings/sources) · [How connecting works](/integrations/connector-catalogue) · [Create a workflow](https://app.vortexiq.ai/workbench/flows/create?connector=azure-devops)

<CapabilityPage data={{"name": "Azure DevOps", "reads": 420, "writes": 416, "stats": [["24", "performance signals"], ["3", "automated checks"], ["0", "prepared fixes"], ["0", "proven workflows"], ["836", "API operations"]], "resRows": [{"n": "apidashboarddashboardwidgets", "r": 2, "w": 6}, {"n": "apigitrepositorypullrequestreviewers", "r": 2, "w": 6}, {"n": "apiserviceendpointendpoints", "r": 2, "w": 6}, {"n": "apisymbolrequests", "r": 2, "w": 6}, {"n": "apiauditstreams", "r": 2, "w": 4}, {"n": "apibuildbuilds", "r": 2, "w": 4}, {"n": "apibuilddefinitions", "r": 2, "w": 4}, {"n": "apidashboarddashboards", "r": 2, "w": 4}], "opsPhrase": "over 13,000", "connPhrase": "more than 200", "monitor": [{"n": "Build Success Rate (30d)", "o": "Run operations", "a": "95 / 80", "d": "Builds with result=succeeded / all completed builds (status=completed) queued within 30d, x100, across the top 10 scanned projects.", "nc": false}, {"n": "Commits (30d)", "o": "Grow revenue", "a": "0 / -50", "d": "Count of commits from GET .../repositories/{repositoryId}/commits (author.date within 30d) summed across the top 15 scanned repos; change_pct vs the prior 30d drives vc_deploy_cadence.", "nc": false}, {"n": "Pipeline Health Score", "o": "Grow revenue", "a": "90 / 70", "d": "0-100 composite = 0.4 x build success rate (30d, result=succeeded / completed) + 0.3 x PR-age score (100 at \u003c=3d median open-PR age, 0 at \u003e=14d) + 0.3 x commit-cadence score (100 + commit change_pct vs prior 30d, clamped", "nc": false}, {"n": "Abandoned PRs (30d)", "o": "Protect revenue", "a": "Watch only", "d": "Count of pull requests with status=abandoned and closedDate within the last 30d (searchCriteria.status=abandoned) across the top 15 scanned repos.", "nc": false}, {"n": "Avg Build Duration", "o": "Run operations", "a": "Watch only", "d": "Average of (finishTime - startTime) in milliseconds across status=completed builds queued within the last 30d.", "nc": false}, {"n": "Avg Commits / Active Repo (30d)", "o": "Run operations", "a": "Watch only", "d": "30d commit count / count of scanned repos with at least 1 commit in 30d; concentration of delivery across the fleet.", "nc": false}, {"n": "Build Failure Rate Alert", "o": "Run operations", "a": "95 / 80", "d": "Build success rate (result=succeeded / completed) queued within 30d; fires when it crosses the vc_ci_pass_rate band (bad at \u003c80%).", "nc": false}, {"n": "Builds (30d)", "o": "Run operations", "a": "Watch only", "d": "Count of builds queued within the last 30d from GET .../build/builds across the top 10 scanned projects.", "nc": false}, {"n": "Canceled Builds (30d)", "o": "Protect revenue", "a": "Watch only", "d": "Count of builds with result=canceled, queued within the last 30d, across the top 10 scanned projects.", "nc": false}, {"n": "Commits by Repository", "o": "Run operations", "a": "Watch only", "d": "Commits in the last 30d grouped by repo full name (project/name), top 10 by count.", "nc": false}, {"n": "Completed PRs (30d)", "o": "Grow revenue", "a": "Watch only", "d": "Count of pull requests with status=completed and closedDate within the last 30d (searchCriteria.status=completed) across the top 15 scanned repos.", "nc": false}, {"n": "Daily Commits Trend", "o": "Control risk and change", "a": "Watch only", "d": "Commits grouped by day over the last 30d (author.date), summed across the top 15 scanned repos.", "nc": false}, {"n": "Deploy Cadence Trend", "o": "Grow revenue", "a": "0 / -50", "d": "Daily commit count over the last 30d (author.date), summed across the top 15 scanned repos; value = 30d total, change_pct vs the prior 30d drives vc_deploy_cadence.", "nc": false}, {"n": "Deploy Stall Alert", "o": "Grow revenue", "a": "0 / -50", "d": "Commit-count change_pct vs the prior 30d; fires on a stall per the vc_deploy_cadence band.", "nc": false}, {"n": "Failed Builds (30d)", "o": "Protect revenue", "a": "Watch only", "d": "Count of builds with result=failed and status=completed, queued within the last 30d, across the top 10 scanned projects.", "nc": false}, {"n": "Median Open PR Age", "o": "Run operations", "a": "3 / 14", "d": "Median of (now - creationDate) in days across all status=active pull requests; drives vc_pr_age_days (lower is better).", "nc": false}, {"n": "Most Active Repo (30d)", "o": "Run operations", "a": "Watch only", "d": "The scanned repo with the most commits in the last 30d; value = commit count, detail = repo full name (project/name).", "nc": false}, {"n": "Open PRs by Repository", "o": "Run operations", "a": "Watch only", "d": "Open (status=active) pull requests grouped by repo full name (project/name), top 10 by count.", "nc": false}, {"n": "Open Pull Requests", "o": "Grow revenue", "a": "Watch only", "d": "Count of pull requests with status=active from GET .../pullrequests?searchCriteria.status=active across the top 15 scanned repos.", "nc": false}, {"n": "PR Age Alert", "o": "Run operations", "a": "3 / 14", "d": "Median open-PR age in days; fires when it crosses the vc_pr_age_days band (bad at \u003e14d).", "nc": false}, {"n": "Projects", "o": "Control risk and change", "a": "Watch only", "d": "Count of projects returned by GET /{organization}/_apis/projects (state=wellFormed).", "nc": false}, {"n": "Repositories", "o": "Control risk and change", "a": "Watch only", "d": "Count of repositories returned by GET .../{project}/_apis/git/repositories, summed across the top 10 scanned projects.", "nc": false}, {"n": "Repositories by Project", "o": "Run operations", "a": "Watch only", "d": "Scanned repositories grouped by their parent project name, top 10 by count.", "nc": false}, {"n": "Stale PRs (\u003e14d)", "o": "Run operations", "a": "Watch only", "d": "Count of status=active pull requests whose age (now - creationDate) exceeds 14 days.", "nc": false}], "audits": [{"n": "CI pass rate below 80%", "o": "Run operations", "sev": "high", "s": "Report only", "why": "More than 1 in 5 CI runs is failing. At this rate, a genuinely broken change is more likely to slip through unnoticed (lost in the normal noise of failures) and developers waste time re-running or diagnosing failures that have nothing to do with their own change, slowing the whole team down.", "codes": ["VC-CI-001"]}, {"n": "Delivery cadence dropped more than 50% vs the prior period", "o": "Grow revenue", "sev": "medium", "s": "Report only", "why": "Deploys or merges have dropped by more than half compared to the prior period. That usually means fixes and improvements, including fixes for issues this same audit has flagged elsewhere, are not shipping, which slows the whole team's ability to respond to anything urgent.", "codes": ["VC-CADENCE-001"]}, {"n": "Median open pull-request age above 14 days", "o": "Run operations", "sev": "medium", "s": "Report only", "why": "Half of open pull requests have been waiting more than two weeks. Work that is done but not merged is not delivering any value yet, and the longer a PR sits open, the more it tends to drift from the main branch, making it harder to merge later than it would have been today.", "codes": ["VC-PR-001"]}], "autos": [], "inValidation": 0}} />
