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

# Field Support

> Explore and compare field support across integrations.

export const API_URL = "https://api.tryfinch.com/providers";

export const API_VERSION = "2020-09-17";

export const READ_ENDPOINTS = ["company", "directory", "individual", "employment", "payment", "pay_statement", "pay_group"];

export const EP_LABELS = {
  company: "Company",
  directory: "Directory",
  individual: "Individual",
  employment: "Employment",
  payment: "Payment",
  pay_statement: "Pay Statement",
  pay_group: "Pay Group"
};

export const AUTH_LABELS = {
  api_token: "API",
  oauth: "OAuth",
  assisted: "Assisted",
  credential: "Credentials"
};

export const WRITE_FIELD_LABELS = {
  "company_benefits.read": "Get Company Deductions",
  "company_benefits.create": "Create Company Deductions",
  "company_benefits.update": "Update Company Deductions",
  "individual_benefits.read": "Get Individual Enrollments",
  "individual_benefits.create": "Enroll Individuals",
  "individual_benefits.delete": "Unenroll Individuals"
};

export const WRITE_FIELD_METHODS = {
  "company_benefits.read": "GET",
  "company_benefits.create": "POST",
  "company_benefits.update": "POST",
  "individual_benefits.read": "GET",
  "individual_benefits.create": "POST",
  "individual_benefits.delete": "DEL"
};

export const OPERATION_FIELDS = ["company_benefits.read", "company_benefits.create", "company_benefits.update", "individual_benefits.read", "individual_benefits.create", "individual_benefits.delete"];

export const ACRONYMS = ["fsa", "hsa", "hra", "ira"];

export const BENEFIT_LABEL_OVERRIDES = {
  "hsa_pre": "HSA Pre Tax",
  "hsa_post": "HSA Post Tax"
};

export function titleCase(str) {
  if (BENEFIT_LABEL_OVERRIDES[str]) return BENEFIT_LABEL_OVERRIDES[str];
  return str.replace(/_/g, " ").replace(/\b\w+/g, w => ACRONYMS.includes(w.toLowerCase()) ? w.toUpperCase() : w.charAt(0).toUpperCase() + w.slice(1));
}

export function flattenFields(obj, prefix = "") {
  const result = {};
  if (!obj || typeof obj !== "object") return result;
  for (const [key, val] of Object.entries(obj)) {
    const path = prefix ? `${prefix}.${key}` : key;
    if (val && typeof val === "object" && !Array.isArray(val)) {
      Object.assign(result, flattenFields(val, path));
    } else {
      result[path] = val;
    }
  }
  return result;
}

export function getReadImpls(providers) {
  const impls = [];
  for (const provider of providers) {
    for (const auth of provider.authentication_methods) {
      const supportedFields = auth.supported_fields;
      if (!supportedFields) continue;
      const fields = {};
      let hasFields = false;
      for (const endpoint of READ_ENDPOINTS) {
        if (!supportedFields[endpoint]) continue;
        for (const [field, val] of Object.entries(flattenFields(supportedFields[endpoint]))) {
          if (!field.startsWith("paging")) {
            fields[`${endpoint}.${field}`] = val;
            hasFields = true;
          }
        }
      }
      if (hasFields) {
        impls.push({
          id: provider.id,
          name: provider.display_name,
          icon: provider.icon,
          authType: auth.type,
          fields
        });
      }
    }
  }
  return impls;
}

export function getWriteImpls(providers) {
  const impls = [];
  for (const provider of providers) {
    for (const auth of provider.authentication_methods) {
      const benefitsSupport = auth.benefits_support;
      if (!benefitsSupport || !Object.keys(benefitsSupport).length) continue;
      const fields = {};
      for (const [benefitType, config] of Object.entries(benefitsSupport)) {
        if (!config) continue;
        for (const [group, actions] of Object.entries(config.supported_operations || ({}))) {
          for (const [action, status] of Object.entries(actions || ({}))) {
            if (group === "individual_benefits" && action === "update") continue;
            if (group === "company_benefits" && action === "delete") continue;
            fields[`${benefitType}.${group}.${action}`] = status === "supported";
          }
        }
        for (const [featureKey, featureVal] of Object.entries(config.supported_features || ({}))) {
          if (featureKey === "description") continue;
          if (Array.isArray(featureVal)) {
            for (const item of featureVal) {
              if (typeof item === "string") fields[`${benefitType}.${featureKey}:${item}`] = true;
            }
          } else if (typeof featureVal === "boolean") {
            fields[`${benefitType}.${featureKey}`] = featureVal;
          }
        }
      }
      if (Object.keys(fields).length) {
        impls.push({
          id: provider.id,
          name: provider.display_name,
          icon: provider.icon,
          authType: auth.type,
          fields
        });
      }
    }
  }
  return impls;
}

export function getImpls(providers, view) {
  return view === "org-payroll" ? getReadImpls(providers) : getWriteImpls(providers);
}

export function getSections(impls, view) {
  if (view === "org-payroll") {
    const fieldsByEndpoint = {};
    for (const ep of READ_ENDPOINTS) fieldsByEndpoint[ep] = new Set();
    for (const impl of impls) {
      for (const fieldKey of Object.keys(impl.fields)) {
        const dotIdx = fieldKey.indexOf(".");
        const ep = fieldKey.substring(0, dotIdx);
        if (fieldsByEndpoint[ep]) fieldsByEndpoint[ep].add(fieldKey.substring(dotIdx + 1));
      }
    }
    return READ_ENDPOINTS.map(ep => ({
      endpoint: ep,
      label: EP_LABELS[ep] || ep,
      fields: [...fieldsByEndpoint[ep] || []].sort()
    })).filter(section => section.fields.length);
  }
  const benefitTypes = new Map();
  for (const impl of impls) {
    for (const fieldKey of Object.keys(impl.fields)) {
      const parts = fieldKey.split(".");
      const benefitType = parts[0];
      const rest = parts.slice(1).join(".");
      if (!benefitTypes.has(benefitType)) benefitTypes.set(benefitType, {
        ops: new Set(),
        feats: new Set(),
        freqs: new Set()
      });
      const bucket = benefitTypes.get(benefitType);
      if (OPERATION_FIELDS.includes(rest) || rest.startsWith("company_benefits.") || rest.startsWith("individual_benefits.")) {
        bucket.ops.add(rest);
      } else if (rest.startsWith("frequencies:") || rest === "frequencies") {
        bucket.freqs.add(rest);
      } else {
        bucket.feats.add(rest);
      }
    }
  }
  return [...benefitTypes.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([benefitType, {ops, feats, freqs}]) => ({
    endpoint: benefitType,
    label: titleCase(benefitType),
    subsections: [...ops.size ? [{
      label: "Endpoints",
      fields: OPERATION_FIELDS.filter(f => ops.has(f))
    }] : [], ...freqs.size ? [{
      label: "Frequencies",
      fields: [...freqs].sort()
    }] : [], ...feats.size ? [{
      label: "Enrollment Configs",
      fields: [...feats].sort()
    }] : []],
    fields: [...ops, ...feats, ...freqs].sort()
  }));
}

export const FIELD_NOTES = {
  "gusto.oauth.individual.encrypted_ssn": "Gusto\u2019s API returns SSNs that are encrypted and require special decryption instructions.",
  "workday.api_token.company.departments.name": "Populated with the names of the Workday supervisory organizations for the company.",
  "workday.api_token.company.departments.source_id": "Populated with the IDs of the Workday supervisory organizations. Available upon request.",
  "workday.api_token.directory.department.name": "Populated with the name of the Workday supervisory organization for the worker.",
  "workday.api_token.directory.department.source_id": "Populated with the ID of the Workday supervisory organization for the worker. Available upon request.",
  "workday.api_token.employment.department.name": "Populated with the name of the Workday supervisory organization for the worker."
};

export const PROVIDER_WRITE_NOTES = {
  "justworks.api_token.company_benefits.create": "Ensure the description field is less than 30 characters when creating deductions.",
  "paychex_flex.credential.company_benefits.create": "Employers must first configure their benefits in their Paychex account before creating deductions via Finch."
};

export function getFieldNote(providerId, authType, endpoint, field) {
  const exactKey = `${providerId}.${authType}.${endpoint}.${field}`;
  if (FIELD_NOTES[exactKey]) return FIELD_NOTES[exactKey];
  for (const [pattern, note] of Object.entries(FIELD_NOTES)) {
    if (pattern.endsWith(".*") && exactKey.startsWith(pattern.slice(0, -2))) return note;
  }
  const writeKey = `${providerId}.${authType}.${field}`;
  if (PROVIDER_WRITE_NOTES[writeKey]) return PROVIDER_WRITE_NOTES[writeKey];
  return null;
}

export function fldStatus(impl, endpoint, field) {
  return impl.fields[`${endpoint}.${field}`] === true ? "supported" : "not_supported";
}

export function authClass(type) {
  return "fsm-auth-" + (type || "assisted");
}

export function parseUrlParams() {
  if (typeof window === "undefined") return {
    view: "org-payroll",
    providers: [],
    auth: [],
    endpoints: [],
    benefits: [],
    fields: []
  };
  const params = new URLSearchParams(window.location.search);
  const get = key => {
    const v = params.get(key);
    return v ? v.split(",").map(decodeURIComponent) : [];
  };
  return {
    view: params.get("view") || "org-payroll",
    providers: get("providers"),
    auth: get("auth"),
    endpoints: get("endpoints"),
    benefits: get("benefits"),
    fields: get("fields")
  };
}

export default function FieldSupportMatrix() {
  const [initParams] = useState(parseUrlParams);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [allProviders, setAllProviders] = useState([]);
  const [view, setView] = useState(initParams.view);
  const [selProviders, setSelProviders] = useState(initParams.providers);
  const [selAuth, setSelAuth] = useState(initParams.auth);
  const [selEndpoints, setSelEndpoints] = useState(initParams.endpoints);
  const [selBenefits, setSelBenefits] = useState(initParams.benefits);
  const [selFields, setSelFields] = useState(initParams.fields);
  const [openDrop, setOpenDrop] = useState(null);
  const [provSearch, setProvSearch] = useState("");
  const [benefitSearch, setBenefitSearch] = useState("");
  const [fieldSearch, setFieldSearch] = useState("");
  useEffect(() => {
    fetch(API_URL, {
      headers: {
        "Finch-API-Version": API_VERSION
      }
    }).then(res => {
      if (!res.ok) throw new Error(`API ${res.status}`);
      return res.json();
    }).then(data => {
      setAllProviders(data);
      setLoading(false);
    }).catch(err => {
      setError(err.message);
      setLoading(false);
    });
  }, []);
  useEffect(() => {
    const handler = e => {
      if (openDrop && !e.target.closest("[data-dropdown]")) setOpenDrop(null);
    };
    document.addEventListener("click", handler);
    return () => document.removeEventListener("click", handler);
  }, [openDrop]);
  const topScrollId = "fsm-top-scroll";
  const tableScrollId = "fsm-table-scroll";
  useEffect(() => {
    const top = document.getElementById(topScrollId);
    const table = document.getElementById(tableScrollId);
    if (!top || !table) return;
    let syncing = false;
    const onTopScroll = () => {
      if (!syncing) {
        syncing = true;
        table.scrollLeft = top.scrollLeft;
        syncing = false;
      }
    };
    const onTableScroll = () => {
      if (!syncing) {
        syncing = true;
        top.scrollLeft = table.scrollLeft;
        syncing = false;
      }
    };
    top.addEventListener("scroll", onTopScroll);
    table.addEventListener("scroll", onTableScroll);
    const inner = top.firstElementChild;
    if (inner) inner.style.width = table.scrollWidth + "px";
    const observer = new ResizeObserver(() => {
      if (inner) inner.style.width = table.scrollWidth + "px";
    });
    observer.observe(table);
    return () => {
      top.removeEventListener("scroll", onTopScroll);
      table.removeEventListener("scroll", onTableScroll);
      observer.disconnect();
    };
  }, [loading, view, selProviders, selAuth, selEndpoints, selBenefits, selFields]);
  const leftPanelId = "fsm-left-panel";
  useEffect(() => {
    const el = document.getElementById(tableScrollId);
    if (!el) return;
    let isDown = false, startX = 0, scrollLeft = 0;
    const onDown = e => {
      isDown = true;
      startX = e.pageX - el.offsetLeft;
      scrollLeft = el.scrollLeft;
      el.style.cursor = "grabbing";
      el.style.userSelect = "none";
    };
    const onUp = () => {
      isDown = false;
      el.style.cursor = "grab";
      el.style.userSelect = "";
    };
    const onMove = e => {
      if (!isDown) return;
      e.preventDefault();
      el.scrollLeft = scrollLeft - (e.pageX - el.offsetLeft - startX);
    };
    el.style.cursor = "grab";
    el.addEventListener("mousedown", onDown);
    window.addEventListener("mouseup", onUp);
    window.addEventListener("mousemove", onMove);
    return () => {
      el.removeEventListener("mousedown", onDown);
      window.removeEventListener("mouseup", onUp);
      window.removeEventListener("mousemove", onMove);
    };
  }, [loading, view]);
  useEffect(() => {
    const right = document.getElementById(tableScrollId);
    const left = document.getElementById(leftPanelId);
    if (!right || !left) return;
    const rightRows = right.querySelectorAll("[data-row]");
    const leftRows = left.querySelectorAll("[data-row]");
    for (let i = 0; i < Math.min(rightRows.length, leftRows.length); i++) leftRows[i].style.height = "auto";
    for (let i = 0; i < Math.min(rightRows.length, leftRows.length); i++) leftRows[i].style.height = rightRows[i].getBoundingClientRect().height + "px";
  });
  const switchView = v => {
    setView(v);
    setSelEndpoints([]);
    setSelBenefits([]);
    setSelFields([]);
    setOpenDrop(null);
  };
  const togDrop = name => {
    setOpenDrop(prev => prev === name ? null : name);
    setProvSearch("");
    setBenefitSearch("");
    setFieldSearch("");
  };
  const tog = (arr, setter, val) => setter(arr.includes(val) ? arr.filter(x => x !== val) : [...arr, val]);
  const [copied, setCopied] = useState(false);
  const copyFilterUrl = () => {
    const params = new URLSearchParams();
    if (view !== "org-payroll") params.set("view", view);
    if (selProviders.length) params.set("providers", selProviders.join(","));
    if (selAuth.length) params.set("auth", selAuth.join(","));
    if (selEndpoints.length) params.set("endpoints", selEndpoints.join(","));
    if (selBenefits.length) params.set("benefits", selBenefits.join(","));
    if (selFields.length) params.set("fields", selFields.join(","));
    const query = params.toString();
    const url = window.location.origin + window.location.pathname + (query ? "?" + query : "");
    navigator.clipboard.writeText(url).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  const exportCSV = (impls, filtSections) => {
    const isDeductions = view === "deductions";
    const headers = [isDeductions ? "Benefit Type" : "Endpoint", ...isDeductions ? ["Category"] : [], "Field", ...impls.map(i => `${i.name}${AUTH_LABELS[i.authType] ? ` (${AUTH_LABELS[i.authType]})` : ""}`)];
    const rows = [];
    for (const section of filtSections) {
      if (section.subsections) {
        for (const sub of section.subsections) {
          for (const field of sub.fields) {
            const displayField = WRITE_FIELD_LABELS[field] || field.replace(/^frequencies:/, "");
            rows.push([section.label, sub.label, displayField, ...impls.map(i => fldStatus(i, section.endpoint, field) === "supported" ? "Supported" : "")]);
          }
        }
      } else {
        for (const field of section.fields) {
          const displayField = field.replace(/^pay_statements\./, "");
          rows.push([section.label, displayField, ...impls.map(i => fldStatus(i, section.endpoint, field) === "supported" ? "Supported" : "")]);
        }
      }
    }
    const escape = v => `"${String(v).replace(/"/g, '""')}"`;
    const csv = [headers.map(escape).join(","), ...rows.map(r => r.map(escape).join(","))].join("\n");
    const blob = new Blob([csv], {
      type: "text/csv"
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `finch-field-support-${view}.csv`;
    a.click();
    URL.revokeObjectURL(url);
  };
  if (loading) return <div className="flex items-center justify-center h-64">
      <div className="flex items-center gap-3 text-gray-400 dark:text-gray-500">
        <svg className="animate-spin h-5 w-5" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" /><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" /></svg>
        Loading providers...
      </div>
    </div>;
  if (error) return <div className="p-8 text-red-500">Error: {error}</div>;
  let impls = getImpls(allProviders, view);
  impls.sort((a, b) => a.name.localeCompare(b.name));
  if (view === "deductions") {
    const authPriority = {
      oauth: 0,
      api_token: 1,
      credential: 2,
      assisted: 3
    };
    const byId = new Map();
    for (const impl of impls) {
      const prev = byId.get(impl.id);
      if (!prev || (authPriority[impl.authType] ?? 9) < (authPriority[prev.authType] ?? 9)) byId.set(impl.id, impl);
    }
    impls = [...byId.values()];
  }
  if (selProviders.length) impls = impls.filter(i => selProviders.includes(i.id));
  if (selAuth.length) impls = impls.filter(i => selAuth.includes(i.authType));
  if (view === "org-payroll" && selEndpoints.length) {
    impls = impls.map(impl => {
      const filtered = {};
      for (const [key, val] of Object.entries(impl.fields)) {
        if (selEndpoints.includes(key.substring(0, key.indexOf(".")))) filtered[key] = val;
      }
      return {
        ...impl,
        fields: filtered
      };
    }).filter(impl => Object.values(impl.fields).some(v => v === true));
  }
  if (view === "deductions" && selBenefits.length) {
    impls = impls.map(impl => {
      const filtered = {};
      for (const [key, val] of Object.entries(impl.fields)) {
        if (selBenefits.includes(key.substring(0, key.indexOf(".")))) filtered[key] = val;
      }
      return {
        ...impl,
        fields: filtered
      };
    }).filter(impl => Object.values(impl.fields).some(v => v === true));
  }
  if (view === "deductions" && selEndpoints.length) {
    impls = impls.map(impl => {
      const filtered = {};
      for (const [key, val] of Object.entries(impl.fields)) {
        const rest = key.substring(key.indexOf(".") + 1);
        if (selEndpoints.includes(rest)) filtered[key] = val;
      }
      return {
        ...impl,
        fields: filtered
      };
    }).filter(impl => Object.values(impl.fields).some(v => v === true));
  }
  let allBenefitTypes = [];
  if (view === "deductions") {
    const types = new Set();
    for (const impl of getImpls(allProviders, "deductions")) {
      for (const key of Object.keys(impl.fields)) types.add(key.substring(0, key.indexOf(".")));
    }
    allBenefitTypes = [...types].sort();
  }
  const allSections = getSections(impls, view);
  const allFieldsList = allSections.flatMap(s => s.fields.map(f => ({
    endpoint: s.endpoint,
    label: s.label,
    field: f,
    key: `${s.endpoint}.${f}`
  })));
  let filtSections = allSections;
  if (selFields.length) {
    filtSections = allSections.map(section => {
      const filteredFields = section.fields.filter(f => selFields.includes(`${section.endpoint}.${f}`));
      const filteredSubs = section.subsections ? section.subsections.map(sub => ({
        ...sub,
        fields: sub.fields.filter(f => selFields.includes(`${section.endpoint}.${f}`))
      })).filter(sub => sub.fields.length) : undefined;
      return {
        ...section,
        fields: filteredFields,
        subsections: filteredSubs
      };
    }).filter(s => s.fields.length);
    impls = impls.filter(impl => filtSections.some(s => s.fields.some(f => fldStatus(impl, s.endpoint, f) === "supported")));
  }
  const uniqueProviders = [];
  const seenIds = new Set();
  for (const provider of allProviders) {
    if (seenIds.has(provider.id)) continue;
    const hasData = view === "deductions" ? provider.authentication_methods.some(a => a.benefits_support && Object.keys(a.benefits_support).length) : provider.authentication_methods.some(a => a.supported_fields);
    if (hasData) {
      uniqueProviders.push({
        id: provider.id,
        name: provider.display_name,
        icon: provider.icon
      });
      seenIds.add(provider.id);
    }
  }
  uniqueProviders.sort((a, b) => a.name.localeCompare(b.name));
  const filteredProviders = provSearch ? uniqueProviders.filter(p => p.name.toLowerCase().includes(provSearch.toLowerCase())) : uniqueProviders;
  const filteredBenefits = benefitSearch ? allBenefitTypes.filter(b => b.toLowerCase().includes(benefitSearch.toLowerCase())) : allBenefitTypes;
  const filteredFields = fieldSearch ? allFieldsList.filter(f => f.field.toLowerCase().includes(fieldSearch.toLowerCase())) : allFieldsList;
  const chipBtn = (active, onClick, label, onClear, count) => active ? <button onClick={onClick} className="inline-flex items-center gap-1.5 px-3 py-1.5 border rounded-lg text-[13px] font-medium cursor-pointer transition-all border-[#3E4AE7] bg-[#f0f1ff] text-[#3E4AE7] dark:border-[#7A84FF] dark:bg-[#7A84FF]/15 dark:text-[#7A84FF]">
      {count != null ? `${count} ${label}${count !== 1 ? "s" : ""}` : label}
      <span onClick={e => {
    e.stopPropagation();
    onClear();
  }} className="inline-flex items-center justify-center w-4 h-4 rounded-full text-[11px] bg-[#c7caff] text-[#3E4AE7] dark:bg-[#7A84FF]/30 dark:text-[#7A84FF]">×</span>
    </button> : <button onClick={onClick} className="inline-flex items-center gap-1.5 px-3 py-1.5 border border-gray-200 dark:border-white/[0.08] rounded-lg text-[13px] font-medium cursor-pointer transition-all bg-white dark:bg-white/[0.08] text-gray-600 dark:text-gray-300 hover:border-gray-400 dark:hover:border-white/20">
      <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="8" cy="8" r="6" /><path d="M8 5v6M5 8h6" /></svg>
      {label}
    </button>;
  const dropItem = (key, selected, onClick, children) => <button key={key} onClick={onClick} className={selected ? "fsm-drop-item fsm-drop-item-selected" : "fsm-drop-item"}>
      {children}
      <span style={{
    flex: 1
  }} />
      {selected && <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M6.5 11.5L3 8l1-1 2.5 2.5 5-5 1 1z" /></svg>}
    </button>;
  const dropSearch = (value, onChange, placeholder) => <input type="text" placeholder={placeholder} value={value} onChange={e => onChange(e.target.value)} className="w-full px-3 py-2 border-0 border-b border-gray-200 dark:border-white/[0.08] text-[13px] outline-none bg-transparent text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500" />;
  const dropMenu = (style, children) => <div className="absolute top-full left-0 mt-1 min-w-[250px] max-h-[320px] overflow-y-auto bg-white dark:bg-[#1e1e1e] border border-gray-200 dark:border-white/[0.08] rounded-xl shadow-lg z-50 p-1" style={style}>{children}</div>;
  const methodBadge = method => {
    if (!method) return null;
    const cls = {
      GET: "fsm-method-get",
      POST: "fsm-method-post",
      DEL: "fsm-method-del"
    };
    return <span className={`fsm-method ${cls[method] || cls.GET}`}>{method}</span>;
  };
  return <div style={{
    marginLeft: 0
  }}>
      {}
      <div className="fsm-beta-callout">
        <svg width="18" height="18" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#3b82f6" strokeWidth="1.5" /><text x="10" y="14.5" textAnchor="middle" fontSize="12" fontWeight="600" fill="#3b82f6">i</text></svg>
        Field support is not available for integrations in beta.
      </div>

      {}
      <div className="flex items-center justify-between gap-4 mb-4 flex-wrap">
        <div className="flex items-center gap-2 flex-wrap">
          {}
          <div data-dropdown="provider" className="relative">
            {chipBtn(selProviders.length > 0, () => togDrop("provider"), "Provider", () => setSelProviders([]), selProviders.length || undefined)}
            {openDrop === "provider" && dropMenu({}, <>{dropSearch(provSearch, setProvSearch, "Search providers...")}{filteredProviders.map(p => dropItem(p.id, selProviders.includes(p.id), () => tog(selProviders, setSelProviders, p.id), <>{p.name}</>))}</>)}
          </div>

          {}
          {view === "org-payroll" && <div data-dropdown="auth" className="relative">
              {chipBtn(selAuth.length > 0, () => togDrop("auth"), "Auth Type", () => setSelAuth([]))}
              {openDrop === "auth" && dropMenu({}, Object.entries(AUTH_LABELS).map(([k, v]) => dropItem(k, selAuth.includes(k), () => tog(selAuth, setSelAuth, k), v)))}
            </div>}

          {}
          {view === "org-payroll" && <div data-dropdown="endpoint" className="relative">
              {chipBtn(selEndpoints.length > 0, () => togDrop("endpoint"), "Endpoint", () => setSelEndpoints([]))}
              {openDrop === "endpoint" && dropMenu({}, READ_ENDPOINTS.map(ep => dropItem(ep, selEndpoints.includes(ep), () => tog(selEndpoints, setSelEndpoints, ep), EP_LABELS[ep] || ep)))}
            </div>}

          {}
          {view === "deductions" && <div data-dropdown="benefit" className="relative">
              {chipBtn(selBenefits.length > 0, () => togDrop("benefit"), "Benefit Type", () => setSelBenefits([]), selBenefits.length || undefined)}
              {openDrop === "benefit" && dropMenu({}, <>{dropSearch(benefitSearch, setBenefitSearch, "Search benefit types...")}{filteredBenefits.map(bt => dropItem(bt, selBenefits.includes(bt), () => tog(selBenefits, setSelBenefits, bt), titleCase(bt)))}</>)}
            </div>}

          {}
          {view === "deductions" && <div data-dropdown="endpoint" className="relative">
              {chipBtn(selEndpoints.length > 0, () => togDrop("endpoint"), "Endpoint", () => setSelEndpoints([]), selEndpoints.length || undefined)}
              {openDrop === "endpoint" && dropMenu({}, OPERATION_FIELDS.map(op => dropItem(op, selEndpoints.includes(op), () => tog(selEndpoints, setSelEndpoints, op), WRITE_FIELD_LABELS[op] || op)))}
            </div>}

          {}
          {view === "org-payroll" && <div data-dropdown="field" className="relative">
              {chipBtn(selFields.length > 0, () => togDrop("field"), "Field", () => setSelFields([]), selFields.length || undefined)}
              {openDrop === "field" && dropMenu({
    minWidth: 280,
    maxHeight: 400
  }, <>
                {dropSearch(fieldSearch, setFieldSearch, "Search fields...")}
                {(() => {
    let currentEndpoint = "";
    return filteredFields.map(f => {
      const isNewGroup = f.endpoint !== currentEndpoint;
      if (isNewGroup) currentEndpoint = f.endpoint;
      return <div key={f.key}>
                    {isNewGroup && <div className="px-3 py-1.5 text-[10px] font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider">{f.label}</div>}
                    {dropItem(f.key + "-item", selFields.includes(f.key), () => tog(selFields, setSelFields, f.key), <code className="text-[11px] bg-gray-100 dark:bg-gray-700 px-1 py-0.5 rounded">{f.field}</code>)}
                  </div>;
    });
  })()}
              </>)}
            </div>}
        </div>

        {}
        <div className="flex items-center gap-2">
          <div data-dropdown="viewswitch" className="relative">
            <button onClick={() => togDrop("viewswitch")} className="flex items-center gap-2 px-3 py-2 text-sm font-medium border border-gray-200 dark:border-white/[0.08] rounded-lg bg-white dark:bg-transparent text-gray-800 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-white/5 transition-colors">
              {view === "org-payroll" ? "Org + Payroll" : "Deductions"}
              <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M3 4.5l3 3 3-3" /></svg>
            </button>
            {openDrop === "viewswitch" && <div className="absolute right-0 top-full mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-white/[0.08] rounded-lg shadow-lg z-50 py-1 min-w-[160px]">
              {[["org-payroll", "Org + Payroll"], ["deductions", "Deductions"]].map(([k, l]) => <button key={k} onClick={() => {
    switchView(k);
    setOpenDrop(null);
  }} className={`w-full text-left px-3 py-2 text-sm transition-colors ${view === k ? "bg-gray-100 dark:bg-white/10 text-gray-900 dark:text-gray-100 font-medium" : "text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-white/5"}`}>{l}</button>)}
            </div>}
          </div>
          <span className="fsm-btn-tooltip-wrap">
            <button onClick={copyFilterUrl} className="fsm-csv-btn">
              {copied ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71" /><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71" /></svg>}
            </button>
            <span className="fsm-btn-tooltip">{copied ? "Copied!" : "Copy link with filters"}</span>
          </span>
          <span className="fsm-btn-tooltip-wrap">
            <button onClick={() => exportCSV(impls, filtSections)} className="fsm-csv-btn">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" y1="15" x2="12" y2="3" /></svg>
            </button>
            <span className="fsm-btn-tooltip">Export as CSV</span>
          </span>
        </div>
      </div>

      {}
      {impls.length === 0 ? <div className="text-center py-16 text-gray-400 dark:text-gray-500">No providers match the selected filters.</div> : <div className="border border-gray-200 dark:border-white/[0.08] rounded-xl overflow-hidden dark:bg-white/[0.02]">
          <div className="flex">

            {}
            <div id={leftPanelId} className="flex-shrink-0 w-[250px] border-r border-gray-200 dark:border-white/[0.08] bg-white dark:bg-transparent z-10">
              {}
              <div data-row="header" className="bg-gray-50 dark:bg-white/5 border-b border-gray-200 dark:border-white/[0.08] flex items-end px-3 pb-2 overflow-hidden box-border" style={{
    height: 48
  }} />

              {}
              {view === "org-payroll" && <div data-row="auth" className="py-2 px-3 border-b border-gray-100 dark:border-white/[0.06] text-xs text-gray-500 dark:text-gray-400 font-medium flex items-center overflow-hidden box-border">Authentication Type</div>}

              {}
              {filtSections.map(section => [<div key={`ls-${section.endpoint}`} data-row={`sec-${section.endpoint}`} className="bg-gray-50 dark:bg-white/[0.03] font-semibold text-[13px] text-gray-800 dark:text-gray-200 py-2.5 px-3 border-b border-gray-200 dark:border-white/[0.08] flex items-center overflow-hidden box-border">{section.label}</div>, ...section.subsections ? section.subsections.flatMap(sub => [<div key={`lsub-${section.endpoint}-${sub.label}`} data-row={`sub-${section.endpoint}-${sub.label}`} className="py-2 px-3 pl-5 border-b border-gray-100 dark:border-white/[0.06] text-[11px] font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider flex items-center overflow-hidden box-border">{sub.label}</div>, ...sub.fields.map(field => {
    let displayField = WRITE_FIELD_LABELS[field] || (section.endpoint === "benefits_read" && field.includes(".") ? field.substring(field.indexOf(".") + 1) : field);
    displayField = displayField.replace(/^frequencies:/, "");
    return <div key={`lf-${section.endpoint}.${field}`} data-row={`${section.endpoint}.${field}`} className="py-2 px-3 border-b border-gray-50 dark:border-white/[0.04] text-gray-700 dark:text-gray-300 flex items-center overflow-hidden box-border">
                        {WRITE_FIELD_LABELS[field] ? <span className="text-[13px] truncate flex items-center gap-1.5">{methodBadge(WRITE_FIELD_METHODS[field])}{displayField}</span> : <code className="text-[13px] bg-gray-100 dark:bg-white/5 px-1.5 py-0.5 rounded font-mono truncate">{displayField}</code>}
                      </div>;
  })]) : section.fields.map(field => <div key={`lf-${section.endpoint}.${field}`} data-row={`${section.endpoint}.${field}`} className="py-2 px-3 border-b border-gray-50 dark:border-white/[0.04] text-gray-700 dark:text-gray-300 flex items-center overflow-hidden box-border">
                    <code className="text-[13px] bg-gray-100 dark:bg-white/5 px-1.5 py-0.5 rounded font-mono truncate">{field.replace(/^pay_statements\./, "")}</code>
                  </div>)])}
            </div>

            {}
            <div className="flex-1 overflow-x-auto text-sm" id={tableScrollId}>
              {}
              <div data-row="header" className="flex bg-gray-50 dark:bg-white/5 border-b border-gray-200 dark:border-white/[0.08]" style={{
    minWidth: impls.length * 140,
    height: 48
  }}>
                {impls.map((impl, i) => <div key={`h-${i}`} style={{
    flex: "1 1 0",
    minWidth: 140,
    padding: "8px 12px",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    gap: 6,
    overflow: "hidden"
  }}>
                    {impl.icon && <img src={impl.icon} alt="" style={{
    width: 20,
    height: 20,
    borderRadius: 3,
    objectFit: "contain",
    flexShrink: 0
  }} />}
                    <span style={{
    fontSize: 12,
    fontWeight: 600,
    whiteSpace: "nowrap",
    overflow: "hidden",
    textOverflow: "ellipsis"
  }} className="text-gray-800 dark:text-gray-200">{impl.name}</span>
                  </div>)}
              </div>

              {}
              {view === "org-payroll" && <div data-row="auth" className="flex border-b border-gray-100 dark:border-white/[0.06]" style={{
    minWidth: impls.length * 140
  }}>
                {impls.map((impl, i) => <div key={`a-${i}`} className="flex-1 min-w-[140px] bg-white dark:bg-transparent py-2 px-3 text-center flex items-center justify-center">
                    <span className={authClass(impl.authType)}>{AUTH_LABELS[impl.authType]}</span>
                  </div>)}
              </div>}

              {}
              {filtSections.map(section => {
    const renderFieldRow = field => <div key={`r-${section.endpoint}.${field}`} data-row={`${section.endpoint}.${field}`} className="flex border-b border-gray-50 dark:border-white/[0.04]" style={{
      minWidth: impls.length * 140
    }}>
                    {impls.map((impl, i) => {
      const status = fldStatus(impl, section.endpoint, field);
      return <div key={`c-${i}`} className="flex-1 min-w-[140px] bg-white dark:bg-transparent py-2 px-3 text-center flex items-center justify-center">
                          {(() => {
        const note = getFieldNote(impl.id, impl.authType, section.endpoint, field);
        return status === "supported" ? <span style={{
          display: "inline-flex",
          alignItems: "center",
          gap: 3,
          position: "relative"
        }}>
                                <svg width="18" height="18" viewBox="0 0 20 20"><circle cx="10" cy="10" r="10" fill="#15803d" /><path d="M6 10.5l2.5 2.5 5.5-5.5" stroke="white" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" /></svg>
                                {note && <span className="fsm-tooltip-wrap" style={{
          cursor: "help"
        }}><svg width="14" height="14" viewBox="0 0 16 16" fill="#9ca3af"><circle cx="8" cy="8" r="7" fill="none" stroke="#9ca3af" strokeWidth="1.5" /><text x="8" y="11.5" textAnchor="middle" fontSize="10" fontWeight="600" fill="#9ca3af">i</text></svg><span className="fsm-tooltip">{note}</span></span>}
                              </span> : <span className="text-gray-300 dark:text-gray-600 text-lg">—</span>;
      })()}
                        </div>;
    })}
                  </div>;
    return [<div key={`rs-${section.endpoint}`} data-row={`sec-${section.endpoint}`} className="flex bg-gray-50 dark:bg-white/[0.03] border-b border-gray-200 dark:border-white/[0.08] py-2.5" style={{
      minWidth: impls.length * 140
    }}>
                    <div className="flex-1"> </div>
                  </div>, ...section.subsections ? section.subsections.flatMap(sub => [<div key={`rsub-${section.endpoint}-${sub.label}`} data-row={`sub-${section.endpoint}-${sub.label}`} className="flex border-b border-gray-100 dark:border-white/[0.06] py-2" style={{
      minWidth: impls.length * 140
    }}>
                      <div className="flex-1"> </div>
                    </div>, ...sub.fields.map(renderFieldRow)]) : section.fields.map(renderFieldRow)];
  })}
            </div>
          </div>
        </div>}
    </div>;
}

<FieldSupportMatrix />
