> ## 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.

# Height on Vortex IQ

> Monitor Height performance signals, run automated checks, and prepare approved work through Vortex IQ.

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 Height performance signals, run automated checks, and prepare approved work through Vortex IQ.

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=height)

<CapabilityPage data={{"name": "Height", "reads": 8, "writes": 3, "stats": [["25", "performance signals"], ["6", "automated checks"], ["0", "prepared fixes"], ["0", "proven workflows"], ["11", "API operations"]], "resRows": [{"n": "tasks", "r": 2, "w": 2}, {"n": "lists", "r": 2, "w": 0}, {"n": "activitys", "r": 1, "w": 0}, {"n": "fieldtemplates", "r": 1, "w": 0}, {"n": "taskcomments", "r": 0, "w": 1}, {"n": "users", "r": 1, "w": 0}, {"n": "workspaces", "r": 1, "w": 0}], "opsPhrase": "over 13,000", "connPhrase": "more than 200", "monitor": [{"n": "Abandoned Findings (\u003e14d no movement)", "o": "Protect revenue", "a": "5 / 15", "d": "Open Height tasks with vortexiq_finding_id set, not completed, and lastActivityAt older than 14 days - findings losing money silently. Copilot-accepted suggestions count as movement.", "nc": false}, {"n": "Finding Resolution Rate (90d)", "o": "Run operations", "a": "75 / 50", "d": "Share of audit findings actually fixed via the Height pipeline. \u003c50% means we're filing faster than the team drains. Height runs 5-12 pts higher than Jira when auto-prioritisation is on.", "nc": false}, {"n": "VortexIQ Findings Open", "o": "Run operations", "a": "Merchant rule", "d": "Tasks we filed from audit findings that haven't been resolved yet - the live backlog of things VortexIQ surfaced. Calibrated 20% looser than Jira because Height workspaces clear faster (auto-grouping + AI prioritisation)", "nc": false}, {"n": "Backlog by Priority", "o": "Run operations", "a": "Merchant rule", "d": "Backlog by Priority.", "nc": false}, {"n": "Backlog by Status", "o": "Run operations", "a": "Watch only", "d": "Backlog by Status.", "nc": false}, {"n": "Blocked Tickets", "o": "Run operations", "a": "3 / 8", "d": "Tasks flagged blocked / impeded - the operational queue's stuck list.", "nc": false}, {"n": "Critical Findings Without a Height Task", "o": "Run operations", "a": "Merchant rule", "d": "Critical / high audit findings older than 7 days with no Height task - coverage gap. Auto-dispatch missed these or the merchant disabled it; either way, money on fire.", "nc": false}, {"n": "Cycle Time by Priority", "o": "Run operations", "a": "Merchant rule", "d": "Cycle Time by Priority.", "nc": false}, {"n": "Finding-to-Task Dispatch Lag", "o": "Run operations", "a": "Merchant rule", "d": "How fast we get findings into Height after detection. Slow dispatch = cold leads by the time the ops team sees them.", "nc": false}, {"n": "In Progress", "o": "Run operations", "a": "Watch only", "d": "Tasks with status.state = started - work actively in flight.", "nc": false}, {"n": "Oldest Open (days)", "o": "Run operations", "a": "Merchant rule", "d": "Age of the longest-running open task - backlog-rot signal.", "nc": false}, {"n": "Open Tickets", "o": "Run operations", "a": "Merchant rule", "d": "Total open tasks across the connected workspace - top-of-funnel backlog volume.", "nc": false}, {"n": "Overloaded Assignees (\u003e10 open)", "o": "Run operations", "a": "0 / 1", "d": "People with \u003e10 open tasks - capacity bottleneck candidates.", "nc": false}, {"n": "Sprint Progress", "o": "Run operations", "a": "80 / 50", "d": "Active-cycle burn - uses Height Cycles, the workspace must have Cycles enabled.", "nc": false}, {"n": "Sprint Velocity (avg)", "o": "Run operations", "a": "Merchant rule", "d": "Sprint Velocity (avg) over time.", "nc": false}, {"n": "Tickets by Assignee", "o": "Run operations", "a": "Merchant rule", "d": "Tickets by Assignee.", "nc": false}, {"n": "Avg Cycle Time (days)", "o": "Run operations", "a": "3 / 7", "d": "Avg Cycle Time (days) for the selected period.", "nc": true}, {"n": "Avg Lead Time (days)", "o": "Run operations", "a": "Merchant rule", "d": "Avg Lead Time (days) for the selected period.", "nc": true}, {"n": "Avg Time-to-Fix (days)", "o": "Run operations", "a": "Merchant rule", "d": "Avg Time-to-Fix (days) for the selected period.", "nc": true}, {"n": "Scope Added Mid-Sprint", "o": "Run operations", "a": "Merchant rule", "d": "Scope Added Mid-Sprint for the selected period.", "nc": true}, {"n": "Throughput Trend", "o": "Run operations", "a": "Watch only", "d": "Throughput Trend for the selected period.", "nc": true}, {"n": "Tickets Resolved (7d)", "o": "Run operations", "a": "Merchant rule", "d": "Tickets Resolved (7d) for the selected period.", "nc": true}, {"n": "Unassigned Tickets", "o": "Run operations", "a": "5 / 20", "d": "Unassigned Tickets for the selected period.", "nc": true}, {"n": "VortexIQ Findings In Progress", "o": "Run operations", "a": "Watch only", "d": "VortexIQ Findings In Progress for the selected period.", "nc": true}, {"n": "VortexIQ Findings Resolved", "o": "Run operations", "a": "Watch only", "d": "VortexIQ Findings Resolved for the selected period.", "nc": true}], "audits": [{"n": "API token / credential expired or about to expire", "o": "Control risk and change", "sev": "critical", "s": "Report only", "why": "Once this credential expires, VortexIQ stops seeing ticket and project activity from this tool entirely, and every backlog, capacity and cycle-time finding here goes stale from that moment, silently, until someone notices the numbers stopped updating.", "codes": ["PM-AUTH-001"]}, {"n": "VortexIQ finding resolution rate below 50%", "o": "Grow revenue", "sev": "high", "s": "Report only", "why": "Fewer than half of the tickets VortexIQ has created from its audit findings ever get resolved. That means the issues this dashboard surfaces, broken tracking, catalog problems, security gaps, are being logged but not fixed at better than a coin-flip rate, so the audit is producing visibility without", "codes": ["PM-RESOLUTION-001"]}, {"n": "VortexIQ-created findings abandoned (\u003e14d no status change)", "o": "Protect revenue", "sev": "high", "s": "Report only", "why": "More than 5 tickets VortexIQ created from its own audit findings have sat untouched for over two weeks. Whatever those findings flagged, whether it was a broken tracking pixel, a stockout, or a security gap, is still unresolved, and the audit that caught it is not doing its job if the resulting tick", "codes": ["PM-ABANDONED-001"]}, {"n": "Assignee overloaded (\u003e10 open tickets)", "o": "Protect revenue", "sev": "medium", "s": "Report only", "why": "At least one person is carrying more than 10 open tickets at once. Beyond the burnout risk, this usually means their tickets move slower than the team average simply from context-switching, which shows up later as missed deadlines that look unrelated to the real cause.", "codes": ["PM-CAPACITY-001"]}, {"n": "Backlog blocked-tickets count growing", "o": "Grow revenue", "sev": "medium", "s": "Report only", "why": "Blocked tickets grew more than 50% versus the prior period. Blocked work does not disappear, it queues up, so a growth rate this size now usually means a larger backlog crunch and slower delivery in the weeks ahead, once those tickets eventually unblock all at once.", "codes": ["PM-BACKLOG-001"]}, {"n": "Cycle time degrading \u003e 25% week-over-week", "o": "Run operations", "sev": "medium", "s": "Report only", "why": "Tickets are taking over 25% longer to complete than they did the prior week. A shift this fast usually has a specific, findable cause, catching it this week is far easier than after it has become the new normal and nobody remembers what changed.", "codes": ["PM-CYCLE-001"]}], "autos": [], "inValidation": 0}} />
