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

# Nmbrs

export const IntegrationBlurb = ({children}) => {
  const kids = Array.isArray(children) ? children : [children];
  const hasContent = kids.some(child => {
    if (child == null || child === false) return false;
    if (typeof child === 'string') return child.trim().length > 0;
    return true;
  });
  if (!hasContent) return null;
  return <blockquote style={{
    borderLeft: '3px solid rgb(var(--primary-light) / .3)',
    margin: '0 0 32px 0',
    padding: '10px 16px',
    background: 'rgb(var(--primary-light) / .05)',
    borderRadius: '0 6px 6px 0',
    fontStyle: 'italic'
  }}>
      {children}
    </blockquote>;
};

export const IntegrationMeta = ({homepageUrl, apiReferenceUrls, contactEmail, transport, status, domains, capabilities, notes}) => {
  const hasLinks = homepageUrl || apiReferenceUrls && apiReferenceUrls.length > 0 || contactEmail;
  const hasDomains = domains && domains.length > 0;
  const hasTransport = !!transport;
  const hasStatus = status === 'planned' || status === 'deprecated';
  const hasCapabilities = capabilities && capabilities.length > 0;
  const hasNotes = !!notes;
  if (!hasLinks && !hasDomains && !hasTransport && !hasStatus && !hasCapabilities && !hasNotes) return null;
  const pill = (bg, color, text) => <span style={{
    display: 'inline-flex',
    alignItems: 'center',
    fontSize: '11px',
    fontWeight: 600,
    lineHeight: 1,
    padding: '3px 8px',
    borderRadius: '999px',
    background: bg,
    color: color,
    whiteSpace: 'nowrap'
  }}>
  {text}
</span>;
  const capabilityColors = {
    'read/write': ['var(--colors-accent-light, #ede9fe)', 'var(--colors-accent, #7c3aed)'],
    'read-write': ['var(--colors-accent-light, #ede9fe)', 'var(--colors-accent, #7c3aed)'],
    'read': ['rgb(var(--primary-light) / .15)', 'var(--colors-content-secondary, #6b7280)'],
    'write': ['rgb(var(--primary-light) / .15)', 'var(--colors-content-secondary, #6b7280)']
  };
  const transportColors = {
    'file': ['var(--colors-warning-light, #fef3c7)', 'var(--colors-warning, #b45309)'],
    'api': ['var(--colors-accent-light, #ede9fe)', 'var(--colors-accent, #7c3aed)'],
    'webhook': ['var(--colors-success-light, #dcfce7)', 'var(--colors-success, #16a34a)']
  };
  const statusConfig = {
    'planned': {
      colors: ['var(--colors-warning-light, #fef3c7)', 'var(--colors-warning, #b45309)'],
      text: 'Planned'
    },
    'deprecated': {
      colors: ['rgb(var(--primary-light) / .15)', 'var(--colors-content-secondary, #6b7280)'],
      text: 'Deprecated'
    }
  };
  const label = text => <span style={{
    fontSize: '11px',
    fontWeight: 600,
    textTransform: 'uppercase',
    letterSpacing: '0.05em',
    color: 'var(--colors-content-secondary, #6b7280)',
    whiteSpace: 'nowrap',
    alignSelf: 'start',
    paddingTop: '2px'
  }}>
  {text}
</span>;
  const cell = children => <div style={{
    display: 'flex',
    alignItems: 'center',
    flexWrap: 'wrap',
    gap: '8px'
  }}>
  {children}
</div>;
  return <>
<style>{`
  .integration-meta-link {
    font-size: 13px;
    color: var(--colors-primary);
    text-decoration: none !important;
    border-bottom: none !important;
    box-shadow: none !important;
    position: relative;
  }
  .integration-meta-link:hover {
    text-decoration: none !important;
    border-bottom: none !important;
    box-shadow: none !important;
  }
  .integration-meta-link::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: -1px;
    width: 100%;
    height: 1px;
    background: rgb(var(--primary-light));
    opacity: 0.5;
    transition: opacity 0.15s;
  }
  .integration-meta-link:hover::after {
    opacity: 1;
  }
`}</style>
<div style={{
    display: 'grid',
    gridTemplateColumns: 'max-content 1fr',
    alignItems: 'center',
    gap: '8px 16px',
    padding: '14px 16px',
    borderRadius: '8px',
    background: 'rgb(var(--primary-light) / .05)',
    border: '1px solid rgb(var(--primary-light) / .1)',
    marginBottom: '32px'
  }}>
{hasLinks && <>
{label('Links')}
{cell(<>
{homepageUrl && <a href={homepageUrl} target="\_blank" rel="noopener noreferrer" className="integration-meta-link">
Website
</a>}
{apiReferenceUrls && apiReferenceUrls.map(({url, label: l}) => <a key={url} href={url} target="\_blank" rel="noopener noreferrer" className="integration-meta-link">
{l || 'API Reference'}
</a>)}
{contactEmail && <a href={`mailto:${contactEmail}`} className="integration-meta-link">
{contactEmail}
</a>}
</>)}
</>}

      {hasStatus && <>
        {label('Status')}
        {cell(pill(...statusConfig[status].colors, statusConfig[status].text))}
      </>}

      {hasTransport && <>
        {label('Transport')}
        {cell(pill(...transportColors[transport] || transportColors['api'], transport === 'file' ? 'File' : transport === 'webhook' ? 'Webhook' : 'API'))}
      </>}

      {hasDomains && <>
        {label('Domains')}
        {cell(domains.map(({domain, capability}) => {
    const [capBg, capColor] = capabilityColors[capability] || capabilityColors['read'];
    return <span key={domain} style={{
      display: 'inline-flex',
      alignItems: 'stretch',
      borderRadius: '999px',
      overflow: 'hidden',
      whiteSpace: 'nowrap',
      fontSize: '12px',
      fontWeight: 600,
      lineHeight: 1
    }}>
              <span style={{
      padding: '4px 8px',
      background: 'rgb(var(--primary-light) / .3)',
      color: 'inherit',
      fontFamily: 'monospace'
    }}>{domain}</span>
              <span style={{
      padding: '4px 8px',
      background: capBg,
      color: capColor
    }}>{capability}</span>
            </span>;
  }))}
      </>}

      {hasCapabilities && <>
        {label('Capabilities')}
        <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '4px'
  }}>
          {capabilities.map(({label: capLabel, supported}) => <div key={capLabel} style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    fontSize: '13px'
  }}>
              <span style={{
    fontWeight: 700,
    fontSize: '12px',
    color: supported ? 'var(--colors-success, #16a34a)' : 'var(--colors-error, #dc2626)',
    width: '12px',
    flexShrink: 0
  }}>{supported ? '✓' : '✗'}</span>
              <span style={{
    color: 'inherit'
  }}>{capLabel}</span>
            </div>)}
        </div>
      </>}

      {hasNotes && <>
        {label('Notes')}
        <span style={{
    fontSize: '13px',
    color: 'var(--colors-content-secondary, #6b7280)',
    fontStyle: 'italic'
  }}>{notes}</span>
      </>}
    </div>

</>;
};

export const IntegrationHeader = ({name, category, logoUrl}) => <div style={{
  display: 'flex',
  alignItems: 'center',
  gap: '16px',
  marginBottom: '24px'
}}>
    {logoUrl && <div style={{
  width: '64px',
  height: '64px',
  borderRadius: '12px',
  background: '#ffffff',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  flexShrink: 0,
  boxShadow: '0 0 0 1px rgba(0,0,0,0.08)'
}}>
        <img src={logoUrl} alt={`${name} logo`} style={{
  width: '40px',
  height: '40px',
  objectFit: 'contain'
}} />
      </div>}
    <div>
      <h1 style={{
  margin: 0
}}>{name}</h1>
      <span style={{
  color: 'var(--colors-content-secondary)',
  fontSize: '14px'
}}>
        {category}
      </span>
    </div>
  </div>;

<IntegrationHeader name="Nmbrs" category="Payroll" logoUrl="https://kbmni.upcloudobjects.com/public/systemlogos/nmbrs-logo.svg" />

<IntegrationMeta
  transport="api"
  domains={[
{ domain: "time_report", capability: "read/write" },
{ domain: "employee", capability: "read/write" },
]}
/>

<IntegrationBlurb />

<Card title="Nmbrs changelog" icon="list" href="/changelog/nmbrs">
  Notable changes to the Nmbrs integration over time.
</Card>
