Handleidingen · n8n

Automatische personeelsplanning met n8n, Claude en Google Calendar

Roosters automatisch plannen met n8n, Claude en Google Calendar. Beschikbaarheid, conflicten en notificaties. Voor MKB zonder Excel-chaos.

🟡 Gevorderd — beetje technisch ⏱ 90 minuten
Tools: n8nClaudeGoogle CalendarGoogle SheetsTypeform
Goud-op-zwart illustratie: een personeelspas verbonden met rooster, vacature, checklist en team

Personeelsplanning voor bedrijven met flexibele medewerkers (horeca, zorg, retail, evenementen) kost wekelijks uren: beschikbaarheid uitvragen, diensten verdelen op basis van vaardigheden, rekening houden met voorkeuren en contracturen, en het rooster communiceren. Met n8n en Claude automatiseer je het grootste deel van dit proces.

Deze handleiding bouwt een workflow die wekelijks beschikbaarheid verzamelt via Typeform, Claude een optimaal rooster laat opstellen op basis van vaardigheidsvereisten en beschikbaarheid, en het rooster publiceert in Google Calendar per medewerker.

Wat je nodig hebt

  • n8n (cloud of self-hosted)
  • Claude API key (Anthropic Console)
  • Typeform account (beschikbaarheidsformulier, gratis plan volstaat)
  • Google Calendar (een agenda per medewerker of een gedeelde teamagenda)
  • Google Sheets (medewerkersprofielen: vaardigheden, contracturen, voorkeuren)

Stap 1: Medewerkersprofieldatabase opzetten

Maak een Google Sheet 'Medewerkersprofielen' met deze kolommen per medewerker:

// Google Sheets: Medewerkersprofielen kolommen
// A: Naam
// B: E-mail
// C: Contracturen per week
// D: Vaardigheden (kommagescheiden: barista,kassa,keuken)
// E: Voorkeursdagen (ma,di,wo = voorkeur; do,vr = minder voorkeur)
// F: Max diensten per week
// G: Google Calendar ID (persoonlijke agenda)
// H: Actief (ja/nee)

// Voorbeeld rij:
// Sara Jansen | sara@cafe.nl | 24 | barista,kassa | ma,di,wo | 3 | sara@gmail.com | ja

Stap 2: Typeform beschikbaarheidsformulier

Maak in Typeform een wekelijks beschikbaarheidsformulier. Vragen: Naam (dropdown met vaste lijst), welke dagen ben je beschikbaar volgende week (meerkeuze: ma t/m zo), welke tijdblokken per dag (ochtend/middag/avond), bijzonderheden of verzoeken (vrije tekst). Het formulier gaat elke donderdagmiddag automatisch uit via n8n.

// Typeform webhook response voorbeeld
{
  "form_response": {
    "answers": [
      { "field": { "ref": "naam" }, "choice": { "label": "Sara Jansen" } },
      { "field": { "ref": "beschikbaar_dagen" }, "choices": { "labels": ["Maandag", "Dinsdag", "Vrijdag"] } },
      { "field": { "ref": "tijdblok_ma" }, "choices": { "labels": ["Ochtend", "Middag"] } },
      { "field": { "ref": "opmerkingen" }, "text": "Vrijdag alleen ochtend ivm tandarts" }
    ]
  }
}

Stap 3: Beschikbaarheid verzamelen in Google Sheets

Voeg een n8n Typeform Trigger node toe die reageert op elke ingediende respons. Sla de beschikbaarheidsdata op in een tweede tabblad 'Beschikbaarheid' met week-ID, naam en beschikbare tijdslots. Zo bouw je wekelijks de inputdata op voor Claude.

Stap 4: Roostergenerator workflow (zaterdag 08:00)

Maak een tweede workflow met Schedule Trigger op zaterdag 08:00. Deze workflow verzamelt alle data en laat Claude het rooster opstellen:

# Cron: elke zaterdag om 08:00
0 8 * * 6

Stap 5: Data ophalen en samenvatten

Haal beschikbaarheidsdata en medewerkersprofielen op uit Google Sheets en combineer ze in een Code node:

const profielen = $('Google Sheets Profielen').all();
const beschikbaarheid = $('Google Sheets Beschikbaarheid').all();

// Volgende week datums berekenen
const maandag = new Date();
maandag.setDate(maandag.getDate() + (8 - maandag.getDay()) % 7 + 1);

// Medewerkers koppelen aan beschikbaarheid
const medewerkersData = profielen
  .filter(p => p.json['Actief'] === 'ja')
  .map(p => {
    const naam = p.json['Naam'];
    const beschikbaar = beschikbaarheid
      .filter(b => b.json['Naam'] === naam)
      .map(b => ({ dag: b.json['Dag'], tijdblok: b.json['Tijdblok'] }));
    return {
      naam,
      contractUren: p.json['Contracturen'],
      vaardigheden: p.json['Vaardigheden'],
      maxDiensten: p.json['Max diensten'],
      beschikbaar
    };
  });

return [{ json: { medewerkersData, weekStart: maandag.toISOString().split('T')[0] } }];

Stap 6: Claude stelt het rooster op

Stuur de gecombineerde data naar Claude met een gedetailleerd planningsprompt:

{
  "model": "claude-sonnet-4-6",
  "max_tokens": 2000,
  "messages": [{
    "role": "user",
    "content": "Maak een optimaal weekrooster voor week van {{ $json.weekStart }}.\n\nBEDRIJFSVEREISTEN:\n- Maandag t/m vrijdag: 2 medewerkers per dienst (ochtend 08:00-14:00, middag 14:00-20:00)\n- Zaterdag: 3 medewerkers (10:00-18:00)\n- Elke dienst: minimaal 1 barista vereist\n\nMEDEWERKERS EN BESCHIKBAARHEID:\n{{ JSON.stringify($json.medewerkersData, null, 2) }}\n\nREGELS:\n1. Respecteer beschikbaarheid strikt\n2. Houd rekening met contracturen (verdeel eerlijk)\n3. Prioriteer vaardigheidsmatch\n4. Houd maximale diensten per medewerker aan\n5. Vermeld als een dienst onderbezet is (onvoldoende beschikbaarheid)\n\nOutput als JSON array van diensten: [{ datum, startTijd, eindTijd, medewerkers: [naam], opmerkingen }]"
  }]
}

Stap 7: Rooster parsen en valideren

Voeg een Code node toe die de Claude JSON response parsed en valideert op compleetheid:

const response = $input.first().json.content[0].text;
const jsonString = response.replace(/```json\n?/g,'').replace(/```/g,'').trim();
const rooster = JSON.parse(jsonString);

// Validatie: zijn alle diensten gevuld?
const onderbezet = rooster.filter(d => d.medewerkers.length < 2);

return [{
  json: {
    rooster,
    isCompleet: onderbezet.length === 0,
    waarschuwingen: onderbezet.map(d => `${d.datum} ${d.startTijd}: te weinig medewerkers`)
  }
}];

Stap 8: Diensten toevoegen aan Google Calendar

Voeg voor elke dienst in het rooster een Google Calendar event toe. Gebruik een Loop node om alle diensten te doorlopen:

// Google Calendar Create Event
{
  "calendarId": "primary",
  "start": {
    "dateTime": "{{ $json.datum }}T{{ $json.startTijd }}:00",
    "timeZone": "Europe/Amsterdam"
  },
  "end": {
    "dateTime": "{{ $json.datum }}T{{ $json.eindTijd }}:00",
    "timeZone": "Europe/Amsterdam"
  },
  "summary": "Dienst: {{ $json.medewerkers.join(', ') }}",
  "description": "Teamrooster\nMedewerkers: {{ $json.medewerkers.join('\n') }}\n{{ $json.opmerkingen || '' }}"
}

Stap 9: Rooster mailen naar medewerkers

Stuur elke medewerker een persoonlijk overzicht van hun diensten de komende week. Gebruik een Code node om per medewerker hun eigen diensten te filteren, gevolgd door een Gmail node:

// Maak persoonlijke rooster-mail per medewerker
const rooster = $('Claude Parse').item.json.rooster;
const medewerkersLijst = [...new Set(rooster.flatMap(d => d.medewerkers))];

return medewerkersLijst.map(naam => {
  const diensten = rooster
    .filter(d => d.medewerkers.includes(naam))
    .map(d => `${d.datum} ${d.startTijd}-${d.eindTijd}`);
  
  const profiel = $('Google Sheets Profielen').all()
    .find(p => p.json['Naam'] === naam);
  
  return {
    json: {
      naam,
      email: profiel?.json['E-mail'],
      diensten: diensten.join('\n'),
      aantalDiensten: diensten.length
    }
  };
});

Stap 10: Manager-rapport bij onderbezetting

Als het rooster onderbezette diensten bevat, stuur je een aparte alert naar de manager met de specifieke diensten die handmatige aanvulling nodig hebben. Voeg een IF node toe voor de check en een e-mail naar management:

Troubleshooting

Probleem: Claude houdt beschikbaarheid niet goed bij

Voeg expliciete constraints toe aan het prompt: 'Je MOET de beschikbaarheid strikt respecteren. Plan NOOIT iemand in op een dag of tijdblok dat niet in hun beschikbaarheidslijst staat. Controleer elk toewijzing expliciet voor je de output geeft.'

Probleem: Niet alle medewerkers vullen het formulier in

Voeg een herinnerings-workflow toe: donderdag 16:00 stuur je een herinnering naar medewerkers die nog niet hebben gereageerd (vergelijk Typeform responses met de medewerkerslijst). Voeg ook een fallback toe in Claude: 'Als beschikbaarheid ontbreekt voor een medewerker, plan ze dan niet in en vermeld dit in de opmerkingen.'

Uitbreidingen

  • Voeg ruiltoewijzingen toe: medewerkers kunnen via een Typeform diensten aanvragen om te ruilen, Claude beoordeelt of de ruil haalbaar is qua vaardigheidsmatch
  • Koppel aan de loonadministratie: gewerkte diensten tellen automatisch mee in de urenstaat
  • Voeg historische data toe: Claude leert na 8 weken welke combinaties van medewerkers goed werken

De tijdsbesparing voor een horecabedrijf met 12 medewerkers: van 3 tot 4 uur wekelijks roosteren naar 30 minuten controle en eventuele aanpassing. Dat is 150 uur per jaar terug die de manager besteedt aan leidinggeven, gasten en kwaliteit in plaats van planningspuzzels.

Gerelateerd

Nieuwsbrief

Meer handleidingen en tips?

Elke week de beste agentic AI inzichten en nieuwe handleidingen in je inbox.

Gratis aanmelden →