KVK Nummer Validatie: Regels, Format en Veelgemaakte Fouten
· KVKBase Team

KVK Nummer Validatie: Regels, Format en Veelgemaakte Fouten

Technische deep-dive in het KVK-nummer format: validatieregels, verschil met vestigingsnummer, regex patronen en veelgemaakte programmeerfouten.

kvkvalidatiedevelopers

KVK Nummer Validatie: Regels, Format en Veelgemaakte Fouten

Het KVK-nummer lijkt simpel: acht cijfers. Maar in de praktijk gaat er verrassend veel mis bij het verwerken van KVK-nummers in software. Van voorloopnullen die verdwijnen tot verwarring met vestigingsnummers — in dit artikel behandelen we alle technische details die je moet weten.

Het KVK-nummer format

Een KVK-nummer (ook wel dossiernummer genoemd) heeft de volgende eigenschappen:

  • Exact 8 cijfers lang
  • Alleen numerieke tekens (0-9)
  • Kan beginnen met een of meer nullen
  • Wordt toegekend bij inschrijving en verandert nooit
  • Is uniek per rechtspersoon

Voorbeelden van geldige KVK-nummers:

12345678
01234567
00123456
80001234

Voorbeelden van ongeldige nummers:

1234567    // Te kort (7 cijfers)
123456789  // Te lang (9 cijfers)
1234567A   // Bevat een letter
12 345 678 // Bevat spaties (tenzij je die eruit filtert)

KVK-nummer vs. vestigingsnummer

Dit is een van de meest voorkomende bronnen van verwarring. Nederland kent twee soorten identificatienummers in het handelsregister:

KVK-nummer (dossiernummer)

  • 8 cijfers
  • Identificeert de rechtspersoon
  • Eén per bedrijf
  • Voorbeeld: 12345678

Vestigingsnummer

  • 12 cijfers
  • Identificeert een specifieke vestiging (locatie)
  • Een bedrijf kan meerdere vestigingen hebben
  • Voorbeeld: 000012345678

Een bedrijf met drie filialen heeft dus:

  • 1 KVK-nummer: 12345678
  • 3 vestigingsnummers: 000012345678, 000012345679, 000012345680

Als je systeem beide typen moet ondersteunen, valideer dan op basis van de lengte:

function identifyNumberType(input) {
  const cleaned = input.replace(/\D/g, '');

  if (cleaned.length === 8) {
    return { type: 'kvk', value: cleaned };
  } else if (cleaned.length === 12) {
    return { type: 'vestiging', value: cleaned };
  } else {
    return { type: 'invalid', value: null };
  }
}

Client-side validatie met regex

Een goede regex voor KVK-nummervalidatie:

// Basis: exact 8 cijfers
const KVK_REGEX = /^\d{8}$/;

// Met optionele spaties en puntjes (die je eruit filtert)
function validateKvkNumber(input) {
  if (!input || typeof input !== 'string') {
    return { valid: false, error: 'Voer een KVK-nummer in' };
  }

  // Verwijder spaties, puntjes en streepjes
  const cleaned = input.replace(/[\s.\-]/g, '');

  if (!KVK_REGEX.test(cleaned)) {
    if (cleaned.length < 8) {
      return { valid: false, error: 'KVK-nummer is te kort (8 cijfers nodig)' };
    }
    if (cleaned.length > 8) {
      return { valid: false, error: 'KVK-nummer is te lang (8 cijfers nodig)' };
    }
    return { valid: false, error: 'KVK-nummer mag alleen cijfers bevatten' };
  }

  // Aanvullende controle: niet alleen nullen
  if (cleaned === '00000000') {
    return { valid: false, error: 'Ongeldig KVK-nummer' };
  }

  return { valid: true, value: cleaned };
}

Python-voorbeeld

import re

def validate_kvk_number(kvk_number: str) -> tuple[bool, str]:
    """Valideer een KVK-nummer op format."""
    if not kvk_number:
        return False, "Voer een KVK-nummer in"

    cleaned = re.sub(r'[\s.\-]', '', kvk_number)

    if not re.match(r'^\d{8}$', cleaned):
        return False, f"Ongeldig format: '{kvk_number}' (verwacht 8 cijfers)"

    if cleaned == '00000000':
        return False, "Ongeldig KVK-nummer"

    return True, cleaned

Veelgemaakte fouten

1. Opslaan als integer

De meest voorkomende fout. Als je een KVK-nummer opslaat als INT of BIGINT in je database, verdwijnen de voorloopnullen:

-- FOUT: voorloopnullen verdwijnen
CREATE TABLE companies (
  kvk_number INT PRIMARY KEY  -- 01234567 wordt 1234567
);

-- GOED: gebruik een tekstveld
CREATE TABLE companies (
  kvk_number CHAR(8) PRIMARY KEY  -- 01234567 blijft 01234567
);

In JavaScript is dit probleem nog subtieler:

// FOUT: wordt als octaal getal geinterpreteerd in sommige contexten
const kvk = 01234567; // Niet wat je verwacht!

// GOED: altijd als string
const kvk = "01234567";

// FOUT: JSON.parse kan problemen geven als de bron ongestructureerd is
// GOED: zorg dat je API altijd strings retourneert voor KVK-nummers

2. Geen input-sanitatie

Gebruikers voeren KVK-nummers in op allerlei manieren:

12345678       // Correct
12.34.56.78    // Met puntjes
12 345 678     // Met spaties
KVK: 12345678  // Met prefix
012-345-678    // Met streepjes en 9 tekens

Bouw altijd een sanitatiestap in:

function sanitizeKvkInput(input) {
  // Verwijder alles behalve cijfers
  return input.replace(/\D/g, '').padStart(8, '0').slice(0, 8);
}

Let op: de padStart hierboven is alleen veilig als je zeker weet dat het om een KVK-nummer gaat waarvan de nullen zijn verdwenen. In de meeste gevallen wil je bij een te kort nummer liever een foutmelding geven dan aanvullen.

3. Niet server-side verifieren

Client-side validatie controleert alleen het format. Het zegt niets over of het nummer daadwerkelijk bestaat in het handelsregister. Een nummer als 99999999 passeert de regex-check, maar hoort mogelijk bij geen enkel bedrijf.

Verifieer daarom altijd server-side via een API-aanroep:

async function verifyKvkNumber(kvkNumber) {
  const formatCheck = validateKvkNumber(kvkNumber);
  if (!formatCheck.valid) {
    return { verified: false, error: formatCheck.error };
  }

  try {
    const response = await fetch(
      `https://api.kvkbase.nl/api/v1/lookup/${formatCheck.value}`,
      { headers: { 'Authorization': `Bearer ${API_KEY}` } }
    );

    if (response.status === 404) {
      return { verified: false, error: 'KVK-nummer niet gevonden' };
    }

    const data = await response.json();
    return {
      verified: true,
      company: data.tradeName,
      isActive: data.isActive
    };
  } catch (error) {
    return { verified: false, error: 'Verificatie mislukt, probeer later opnieuw' };
  }
}

4. Actieve en inactieve bedrijven niet onderscheiden

Een KVK-nummer kan toebehoren aan een bedrijf dat is uitgeschreven. Dit is cruciaal voor facturatie en contracten. Controleer altijd de status:

const result = await verifyKvkNumber('12345678');
if (result.verified && !result.isActive) {
  showWarning('Dit bedrijf is uitgeschreven bij de KVK');
}

5. Geen foutmeldingen voor de gebruiker

Als validatie faalt, geef dan een duidelijke, specifieke foutmelding. “Ongeldig nummer” is niet genoeg. Vertel de gebruiker wat er mis is en wat er verwacht wordt.

Database-design aanbevelingen

CREATE TABLE companies (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  kvk_number CHAR(8) NOT NULL UNIQUE,
  vestiging_number CHAR(12),
  trade_name VARCHAR(255) NOT NULL,
  is_active BOOLEAN DEFAULT true,
  verified_at TIMESTAMP WITH TIME ZONE,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

  CONSTRAINT valid_kvk_format CHECK (kvk_number ~ '^\d{8}$'),
  CONSTRAINT valid_vestiging_format
    CHECK (vestiging_number IS NULL OR vestiging_number ~ '^\d{12}$')
);

CREATE INDEX idx_companies_kvk ON companies(kvk_number);

Conclusie

KVK-nummervalidatie is een klein onderdeel van je applicatie, maar fouten hierin hebben grote gevolgen. Door het nummer altijd als string op te slaan, input te sanitiseren, client-side en server-side te valideren en de bedrijfsstatus te controleren, voorkom je de meest voorkomende problemen.

Met KVKBase combineer je formatvalidatie en bestaanscontrole in een enkele API-aanroep, zodat je zeker weet dat het nummer niet alleen geldig is, maar ook daadwerkelijk bij een (actief) bedrijf hoort.