Cortex handelt een no-show af vanuit één chatcommando: afspraak annuleren (declarabiliteits-nudge), en de openstaande concept- huisartsbrief wordt via LLM herschreven en ter review aangeboden in een document artifact (human-in-the-loop, PATCH dispatch zet status op verzendklaar). - API-routes: context, rescript, cancel, dispatch - NoShowDocumentBlock: review/edit UI met origineel-vergelijk - Mock-data voor concept huisartsbrief - PRD, FO, bouwplan en epics in docs/intent/noshow-case/ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
24 KiB
Bouwplan — Cortex Casus: No Show Afhandeling
Projectnaam: Cortex — Casus 2 (No Show) Versie: v1.0 Datum: 2026-03-29 Auteur: Colin Lit / Antigravity
1. Doel en Context
Doel: Een werkende demonstratie bouwen van de no-show afhandelings-flow: van één getypte zin ("patiënt niet verschenen") tot een AI-herschreven, geaccordeerde huisartsbrief — zonder dat de regiebehandelaar een enkel scherm verlaat.
Context: De V2 Cortex architectuur (three-layer: Reflex → Orchestrator → Nudge) is volledig operationeel. De chat pipeline, artifact store en nudge infrastructure zijn live. Dit bouwplan bouwt de no-show casus bovenop die bestaande foundation.
Kernprincipe:
"Eén zin van de zorgverlener vervangt vijf administratieve handelingen."
Beoogd resultaat: Een demonstreerbare end-to-end flow die:
register_no_showintent herkent (Reflex + Orchestrator)- Declarabiliteitsregel proactief signaleert (Nudge stap 1)
- Na 1-klik annulering de conceptbrief opspoort (Nudge stap 2)
- De brief via LLM herschrijft en toont in Artifact (Document Block)
- Accordering en
ready_for_dispatchstatus afhandelt
Referenties:
- PRD:
docs/intent/noshow-case/prd-cortex-casus-noshow.md - FO:
docs/intent/noshow-case/fo-cortex-casus-noshow.md - Bestaand bouwplan V2:
docs/intent/bouwplan-cortex-v2.md
2. Dev Quick Start
Codebase oriëntatie
lib/cortex/
├── types.ts # ✅ UITBREIDEN — register_no_show intent + BlockType
├── reflex-classifier.ts # ✅ UITBREIDEN — no-show patronen toevoegen
├── nudge.ts # ✅ UITBREIDEN — 2 nieuwe rules toevoegen
└── noshow-handler.ts # 🆕 NIEUW in E3 — multi-step state machine
stores/
└── cortex-store.ts # ✅ UITBREIDEN — noShowFlow state in E3
components/cortex/
├── blocks/
│ └── noshow-document-block.tsx # 🆕 NIEUW in E4 — document artifact component
└── chat/
└── nudge-chat-message.tsx # ✅ HERGEBRUIK — werkt al met accept/dismiss
app/api/cortex/
└── noshow/
├── cancel/route.ts # 🆕 NIEUW in E3 — annuleer met cancelled_no_show
├── context/route.ts # 🆕 NIEUW in E3 — concept brief ophalen
└── rescript/route.ts # 🆕 NIEUW in E3 — LLM brief herschrijven
Werkwijze per story
- Lees story + done criteria
- Check bestaande code (zie "Bestaande code" sectie per epic)
- Implementeer — kleinste werkende stap
- Run
pnpm lintenpnpm build - Commit:
feat(cortex): NS.E{n}.S{n} — <beschrijving>
Conventies
- TypeScript strict mode — geen
any - Nederlandse gebruikersteksten, Engelse code/comments
- Zod voor alle API validatie
- Mock data voor prototype — geen echte Zorgmail/declaratie koppeling
- Graceful degradation: als LLM faalt, toon originele brief + foutmelding
3. Architecturele Keuzes
3.1 Multi-step flow: hoe?
De no-show flow heeft 5 sequentiële stappen waarbij elke stap afhangt van de vorige keuze. De huidige nudge engine is single-shot (trigger → 1 nudge → klaar).
Gekozen aanpak: Lightweight state machine in de store
noShowFlowState: {
step: 'idle' | 'waiting_cancel' | 'waiting_brief' | 'brief_open' | 'done'
appointmentId: string | null
documentId: string | null
originalBriefContent: string | null
}
Bij elke nudge-accept checkt de chat-panel welke step actief is en dispatcht de juiste vervolgactie. Dit hergebruikt de bestaande nudge-accept flow zonder een compleet nieuwe state machine framework nodig te hebben.
Niet gekozen: Een aparte state machine library (XState) — te zwaar voor prototype scope.
3.2 Document Block: Rich Text Editor?
De FO noemt een rich-text-editor.tsx, maar die bestaat niet in de codebase. Voor prototype scope is een gewone <Textarea> voldoende. De "kwaliteit" van de brief zit in de LLM output, niet in de editor.
Besluit: <Textarea> hergebruiken (zoals in dagnotitie-block.tsx), focus op Akkoord-knop en status-update.
3.3 Mock data strategie
Twee mock objecten toevoegen in lib/cortex/mock-data/noshow.ts:
- Een declarabele afspraak voor de actieve patiënt
- Een concept huisartsbrief gekoppeld aan die patiënt
De API routes checken eerst op mock data voordat ze Supabase bevragen — zodat de demo altijd werkt.
4. Epics & Stories Overzicht
| Epic ID | Titel | Doel | Stories | Geschatte tijd |
|---|---|---|---|---|
| NS.E1 | Intent Foundation | Type + classifier | 3 | ~1u |
| NS.E2 | Nudge Rules | 2 rules + chained flow | 3 | ~2u |
| NS.E3 | No-Show API | Cancel + brief-context + LLM rescript | 4 | ~3u |
| NS.E4 | Document Artifact | Block component + Akkoord-knop | 3 | ~2u |
| NS.E5 | Integration & Demo | End-to-end flow + polish | 3 | ~2u |
Totaal: 16 stories, ~10u bouwtijd
5. Epics & Stories (Gedetailleerd)
NS.E1 — Intent Foundation
Doel: register_no_show herkenbaar maken voor het volledige classificatie systeem.
Bestaande code om te lezen voor je begint:
lib/cortex/types.ts—CortexIntentunion type +BLOCK_CONFIGSlib/cortex/reflex-classifier.ts— patroon hoe andere intents worden gematcht
NS.E1.S1 — CortexIntent type uitbreiden
Taak: register_no_show toevoegen aan de type definities.
Aanpassingen:
lib/cortex/types.ts:- Voeg
'register_no_show'toe aanCortexIntentunion - Voeg
'register_no_show'toe aanBLOCK_CONFIGSmet{ type: 'register_no_show', title: 'No Show Registratie', size: 'md', icon: 'UserX' } BlockTypewordt automatisch meegenomen (afgeleid vanCortexIntent)
- Voeg
Done criteria:
pnpm buildslaagt zonder type errorsregister_no_showis een geldigeCortexIntentenBlockType
NS.E1.S2 — Reflex Classifier patronen
Taak: Snelle (< 20ms) lokale patroonherkenning voor no-show input.
Aanpassingen:
lib/cortex/reflex-classifier.ts— voeg patronen toe aan de intent-map:'register_no_show': [ /no.?show/i, /niet verschenen/i, /niet gekomen/i, /afwezig.*afspraak/i, /no show/i, /komt niet op/i, ]- Confidence:
0.85bij directe match (hoog — weinig ambiguïteit verwacht)
Done criteria:
classifyWithReflex("patiënt is niet verschenen")geeft{ intent: 'register_no_show', confidence: 0.85 }classifyWithReflex("no show vandaag")geeft hetzelfde resultaat- Input zonder patroon escaleert correct naar AI
NS.E1.S3 — Orchestrator system prompt uitbreiden
Taak: Orchestrator (Layer 2) informeren over de nieuwe intent.
Aanpassingen:
lib/cortex/orchestrator.tsof hetbuildSystemPromptinapp/api/cortex/chat/route.ts:- Voeg
register_no_showtoe aan de intent-beschrijvingen:register_no_show: Gebruik wanneer de zorgverlener aangeeft dat een patiënt niet op de afspraak is verschenen. Signaalwoorden: "no show", "niet verschenen", "niet gekomen", "afwezig bij afspraak".
- Voeg
Done criteria:
- System prompt bevat
register_no_showmet correcte beschrijving - AI classificeert "cliënt was er niet" als
register_no_show(handmatig te testen via chat)
NS.E2 — Nudge Rules & Chained Flow
Doel: Twee proactieve nudges aanmaken die sequentieel volgen op de no-show registratie.
Bestaande code om te lezen voor je begint:
lib/cortex/nudge.ts—ProtocolRuleinterface +PROTOCOL_RULESarray +evaluateNudge()components/cortex/chat/nudge-chat-message.tsx— hoe accept/dismiss werktcomponents/cortex/chat/chat-panel.tsx— hoeevaluateNudgewordt aangeroepen na action completionstores/cortex-store.ts—noShowFlowStateis hier nog niet — toevoegen in S1
NS.E2.S1 — No-show flow state aan store toevoegen
Taak: Minimale state machine voor de 5-staps flow in de Zustand store.
Aanpassingen:
stores/cortex-store.ts:// Voeg toe aan state interface: noShowFlow: { step: 'idle' | 'waiting_cancel' | 'waiting_brief' | 'brief_open' | 'done'; appointmentId: string | null; documentId: string | null; originalContent: string | null; }; // Actions: setNoShowStep: (step: NoShowStep) => void; setNoShowContext: (ctx: Partial<NoShowFlowContext>) => void; resetNoShowFlow: () => void;- Initiële waarde:
{ step: 'idle', appointmentId: null, documentId: null, originalContent: null }
Done criteria:
- Store compileert zonder errors
useCorteStore(s => s.noShowFlow)geeft initiële state terug
NS.E2.S2 — Nudge Rule 1: Declarabiliteitscheck
Taak: Na register_no_show intent → nudge die waarschuwt over declarabele afspraak.
Aanpassingen:
lib/cortex/nudge.ts— voeg toe aanPROTOCOL_RULES:{ id: 'noshow-declarabel-check', trigger: { intent: 'register_no_show', }, priority: 'high', suggestion: { intent: 'cancel_appointment', message: 'Ik zie een declarabel consult in de agenda. Volgens inkoopvoorwaarden mag deze afspraak NIET gedeclareerd worden. Wil je dat ik deze annuleer als \'No Show\'?', prefillEntities: {}, }, expiresAfterMs: 5 * 60 * 1000, }- Let op:
priority: 'high'— deze nudge verschijnt altijd als eerste
Done criteria:
evaluateNudge({ intent: 'register_no_show', ... })geeft nudge met idnoshow-declarabel-checkterug- Nudge toont correct in chat UI met
[Ja, annuleer]accept-knop
NS.E2.S3 — Nudge Rule 2: Concept Brief Alert + Chained Trigger
Taak: Na acceptatie van Nudge 1 → check op concept brief → Nudge 2 tonen.
Aanpassingen:
Stap A — Nudge Rule 2 definiëren in lib/cortex/nudge.ts:
{
id: 'noshow-brief-check',
trigger: {
intent: 'cancel_appointment',
conditions: [{ field: 'noShowContext', operator: 'exists', value: true }],
},
priority: 'high',
suggestion: {
intent: 'register_no_show', // hergebruik als signaal voor brief-stap
message: 'Afspraak geannuleerd. Er staat nog een concept huisartsbrief klaar. Zal ik daar de No Show in verwerken?',
prefillEntities: {},
},
expiresAfterMs: 5 * 60 * 1000,
}
Stap B — Chained accept logica in components/cortex/chat/chat-panel.tsx:
- Bij
acceptSuggestionvannoshow-declarabel-check:setNoShowStep('waiting_cancel')- Roep
/api/cortex/noshow/cancelaan - Roep
/api/cortex/noshow/contextaan (check op conceptbrief) - Als brief gevonden:
setNoShowContext({ documentId, originalContent })+evaluateNudgevoor rule 2 setNoShowStep('waiting_brief')
- Bij
acceptSuggestionvannoshow-brief-check:- Roep
/api/cortex/noshow/rescriptaan openArtifact({ type: 'register_no_show', ... })met herschreven inhoudsetNoShowStep('brief_open')
- Roep
Done criteria:
- Klikken op
[Ja, annuleer]triggert de cancel API call + toont Nudge 2 - Klikken op
[Ja, pas brief aan]triggert rescript API + opent artifact noShowFlow.stepwerkt correct door alle stappen
NS.E3 — No-Show API Routes
Doel: Drie backend routes bouwen die de no-show flow ondersteunen.
Bestaande code om te lezen voor je begint:
app/api/cortex/agenda/cancel/route.ts— bestaand cancel patroonapp/api/cortex/chat/route.ts— Anthropic streaming patroonlib/auth/server.ts—createClient()voor authenticated requests
NS.E3.S1 — Mock data aanmaken
Taak: Statische mock data voor declarabele afspraak en concept huisartsbrief.
Nieuw bestand: lib/cortex/mock-data/noshow.ts
export const MOCK_NO_SHOW_APPOINTMENT = {
id: 'mock-appt-noshow-001',
patientId: 'demo-patient-001', // matcht actieve demo patiënt
date: new Date().toISOString(),
type: 'intake_consult',
is_billable: true,
status: 'scheduled',
title: 'Intake Consult',
};
export const MOCK_CONCEPT_BRIEF = {
id: 'mock-brief-001',
patientId: 'demo-patient-001',
type: 'huisartsbrief',
status: 'concept',
title: 'Huisartsbrief n.a.v. intake',
content: `Geachte collega,
Hierbij informeer ik u over de intake van uw patiënt die bij ons in zorg is gekomen.
Tijdens de intake is uitgebreid de hulpvraag besproken en zijn de volgende aandachtspunten
naar voren gekomen: [...]
Het vervolgtraject bestaat uit wekelijkse gesprekken gericht op [...]
Met vriendelijke groet,
[Behandelaar]`,
createdAt: new Date().toISOString(),
};
Done criteria:
- Bestand bestaat en exporteert beide mock objecten
- TypeScript compileert zonder errors
NS.E3.S2 — Cancel Route (/api/cortex/noshow/cancel)
Taak: Afspraak annuleren met cancelled_no_show status.
Nieuw bestand: app/api/cortex/noshow/cancel/route.ts
Request body (Zod):
const CancelNoShowSchema = z.object({
appointmentId: z.string(),
patientId: z.string(),
});
Response:
{ success: true, appointmentId: string, newStatus: 'cancelled_no_show' }
Logica:
- Auth check via
createClient() - Zod validatie
- Check op mock data (
MOCK_NO_SHOW_APPOINTMENT.id) — als match, return mock success - Anders: Supabase update op
encounterstabel,status = 'cancelled_no_show' - Return success response
Done criteria:
POST /api/cortex/noshow/cancelmet mock appointment ID geeft{ success: true }terug- Route geeft correcte 400/401 bij ongeldige input/auth
NS.E3.S3 — Context Route (/api/cortex/noshow/context)
Taak: Openstaande concept documenten ophalen voor actieve patiënt.
Nieuw bestand: app/api/cortex/noshow/context/route.ts
Query params: ?patientId=<id>
Response:
{
hasConceptBrief: boolean;
document?: {
id: string;
title: string;
content: string;
type: string;
};
}
Logica:
- Auth check
patientIdparam ophalen- Check op mock data — als
patientIdmatchtdemo-patient-001, return mock brief - Anders: Supabase query op
reportstabel,type = 'huisartsbrief'+status = 'concept'+patient_id = patientId+deleted_at IS NULL
Done criteria:
GET /api/cortex/noshow/context?patientId=demo-patient-001geeft mock brief terughasConceptBrief: falsebij onbekende patiënt
NS.E3.S4 — Rescript Route (/api/cortex/noshow/rescript)
Taak: LLM herschrijft de huisartsbrief met de no-show professioneel verwerkt.
Nieuw bestand: app/api/cortex/noshow/rescript/route.ts
Request body (Zod):
const RescriptSchema = z.object({
documentId: z.string(),
originalContent: z.string(),
patientId: z.string(),
});
Response:
{ rescriptedContent: string; documentId: string; }
System prompt voor LLM:
Je bent een klinisch schrijver in een GGZ instelling. Je taak is een bestaande conceptbrief
aanpassen zodat de no-show van de patiënt professioneel en contextbewust verwerkt is.
Regels:
- Integreer de no-show in de lopende tekst, voeg het NIET achteraan toe
- Gebruik formele GGZ-taal ("de patiënt is niet verschenen op het geplande consult")
- Bewaar alle andere informatie in de brief
- Voeg een zin toe over herschikken van het vervolggesprek
- Reageer ALLEEN met de herschreven brieftekst, geen uitleg
Done criteria:
- Route roept Anthropic API aan met correct system prompt
- Herschreven brief is contextbewust (no-show verwerkt, niet achteraan geplakt)
- Route faalt graceful (geeft originele content terug + foutmelding bij API error)
NS.E4 — Document Artifact Block
Doel: Een nieuw block component dat de herschreven brief weergeeft met een Akkoord-knop.
Bestaande code om te lezen voor je begint:
components/cortex/blocks/dagnotitie-block.tsx— patroon voor block met textareacomponents/cortex/shared/block-footer.tsx—primaryAction/secondaryActionpropscomponents/cortex/artifacts/artifact-container.tsx—renderArtifactBlock()switch
NS.E4.S1 — NoShowDocumentBlock component
Taak: Block component voor het weergeven en bewerken van de herschreven brief.
Nieuw bestand: components/cortex/blocks/noshow-document-block.tsx
Props interface:
interface NoShowDocumentBlockProps {
prefill: {
documentId: string;
content: string;
title: string;
originalContent?: string;
};
}
UI Structuur:
┌─────────────────────────────────────────────┐
│ 📄 Huisartsbrief — aangepast door Cortex │
│ ─────────────────────────────────────────── │
│ [Textarea met herschreven inhoud] │
│ (editeerbaar, minimaal 12 regels hoog) │
│ │
│ ─────────────────────────────────────────── │
│ [Origineel bekijken ↕] [Akkoord & Verzendklaar →] │
└─────────────────────────────────────────────┘
Logica:
- State:
content(editeerbaar),isSubmitting,isDone - "Origineel bekijken" toggle: toont
originalContentin grijs eronder (collapsible) - "Akkoord & Verzendklaar" knop:
setIsSubmitting(true)PATCH /api/cortex/noshow/dispatch(S2) met{ documentId, finalContent: content }- Bij success:
setIsDone(true),setNoShowStep('done'), toast "Brief verzendklaar" - Bij fout: toast foutmelding,
setIsSubmitting(false)
Done criteria:
- Component rendert herschreven inhoud in editeerbare textarea
- "Akkoord" knop triggert dispatch API call
- Na success toont component bevestiging (geen navigatie nodig)
NS.E4.S2 — Dispatch Route (/api/cortex/noshow/dispatch)
Taak: Document status naar ready_for_dispatch zetten na accordering.
Nieuw bestand: app/api/cortex/noshow/dispatch/route.ts
Request body (Zod):
const DispatchSchema = z.object({
documentId: z.string(),
finalContent: z.string(),
});
Logica:
- Auth check
- Als
documentIdmatcht mock ID → return success (mock) - Anders: Supabase update op
reports,status = 'ready_for_dispatch',content = finalContent
Done criteria:
PATCHmet mock document ID geeft{ success: true, status: 'ready_for_dispatch' }terug
NS.E4.S3 — ArtifactContainer register_no_show toevoegen
Taak: renderArtifactBlock() switch uitbreiden met het nieuwe block.
Aanpassingen:
components/cortex/artifacts/artifact-container.tsx:case 'register_no_show': return ( <NoShowDocumentBlock prefill={artifact.prefill as NoShowDocumentPrefill} /> );- Import toevoegen voor
NoShowDocumentBlock
Done criteria:
openArtifact({ type: 'register_no_show', prefill: { documentId, content, title } })opent het juiste block- Geen TypeScript errors in artifact-container
NS.E5 — Integration & Demo Polish
Doel: De volledige flow end-to-end werkend maken en demo-klaar polijsten.
Bestaande code om te lezen voor je begint:
components/cortex/chat/chat-panel.tsx— de centrale orchestrator voor de hele flowdocs/intent/noshow-case/fo-cortex-casus-noshow.md— de exacte stap-voor-stap flow
NS.E5.S1 — Chat Panel integratie
Taak: chat-panel.tsx uitbreiden met de no-show flow orchestratie.
Aanpassingen in chat-panel.tsx:
// Na handleConfirmAction / na evaluateNudge call:
const handleNudgeAccept = async (suggestion: NudgeSuggestion) => {
const { noShowFlow, setNoShowStep, setNoShowContext } = useCorteStore.getState();
if (suggestion.id === 'noshow-declarabel-check') {
setNoShowStep('waiting_cancel');
// Cancel afspraak
await fetch('/api/cortex/noshow/cancel', { method: 'POST', body: ... });
// Check concept brief
const ctx = await fetch(`/api/cortex/noshow/context?patientId=${activePatient?.id}`).then(r => r.json());
if (ctx.hasConceptBrief) {
setNoShowContext({ documentId: ctx.document.id, originalContent: ctx.document.content });
// Trigger nudge 2 als chat message
addChatMessage({ type: 'nudge', content: BRIEF_NUDGE_MESSAGE, nudge: NOSHOW_BRIEF_NUDGE });
setNoShowStep('waiting_brief');
} else {
setNoShowStep('done');
addChatMessage({ type: 'assistant', content: 'Afspraak geannuleerd als No Show. Er zijn geen openstaande conceptbrieven gevonden.' });
}
}
if (suggestion.id === 'noshow-brief-check') {
setNoShowStep('brief_open');
const { documentId, originalContent } = noShowFlow;
const result = await fetch('/api/cortex/noshow/rescript', { method: 'POST', body: ... }).then(r => r.json());
openArtifact({
type: 'register_no_show',
title: 'Huisartsbrief — aangepast',
prefill: { documentId, content: result.rescriptedContent, title: 'Huisartsbrief', originalContent },
});
addChatMessage({ type: 'assistant', content: 'Huisartsbrief is aangepast. Kijk of je hiermee akkoord bent.' });
}
};
Done criteria:
- Volledige 5-staps flow is doorloopbaar in de browser
- Elke stap produceert de juiste chat message en UI state
NS.E5.S2 — Loading states en foutafhandeling
Taak: Zorgdragen dat de flow nooit "vastloopt" voor de gebruiker.
Aanpassingen:
- Voeg
isNoShowProcessing: booleantoe aan store chat-panel.tsx: toon<ProcessingIndicator>tijdens API calls tussen stappen- Bij API fout: voeg error chat message toe met Nederlandse foutmelding
resetNoShowFlow()aanroepen bij component unmount (cleanup)
Done criteria:
- Loading indicator zichtbaar tijdens elke API call
- Bij netwerk fout verschijnt: "Er is iets misgegaan. Probeer het opnieuw."
- Flow kan opnieuw gestart worden na een fout
NS.E5.S3 — Demo script validatie
Taak: De flow handmatig doorlopen en valideren aan de succescriteria uit het PRD.
Checklist:
- Input "patiënt niet verschenen" →
register_no_showherkend (Reflex, < 20ms) - Input "cliënt was er niet vandaag" → Orchestrator herkent correct
- Nudge 1 verschijnt met juiste declarabiliteits-waarschuwing
- Klik
[Ja, annuleer]→ Cancel API succesvol → Nudge 2 verschijnt - Klik
[Ja, pas brief aan]→ Rescript API → Artifact opent met aangepaste brief - Textarea is editeerbaar — aanpassing blijft behouden
- Klik
[Akkoord & Verzendklaar]→ Dispatch API → bevestiging in chat - Volledige flow duurt < 30 seconden (excl. LLM latency)
- Geen schermwissels nodig gedurende de hele flow
pnpm buildslaagt zonder errors
Done criteria: Alle checkboxen afgevinkt = casus NS volledig gereed.
6. Risico's en Mitigatie
| Risico | Kans | Impact | Mitigatie |
|---|---|---|---|
| LLM rescript kwaliteit onvoldoende | Middel | Hoog | System prompt itereren; originele brief altijd tonen als fallback |
| Multi-step flow breekt bij snelle klikken | Laag | Middel | isNoShowProcessing boolean blokkeert dubbele submits |
| Mock data matcht niet met actieve demo patiënt | Middel | Hoog | patientId hardcoderen in mock als demo-patient-001 — zelfde als in bestaande demo setup |
ArtifactContainer switch mist nieuwe BlockType |
Laag | Hoog | TypeScript exhaustive check dwingt dit af bij pnpm build |
| Nudge 2 triggert niet als brief niet bestaat | Laag | Laag | Flow eindigt graceful met chat message "geen brief gevonden" |
7. Niet in Scope (Prototype)
- Echte declaratie-integratie (ZPM/HiX koppeling)
- Zorgmail verzending van de brief
- Automatisch inplannen van vervangende afspraak
- Meerdere conceptbrieven per patiënt (multi-document select)
- Audit log van de no-show registratie
- Undo/rollback van de annulering