ADR 0006 — In-app enterprise support desk (tickets, SLA, email hooks, KB)
Status
Accepted — 2026-05-15
Context
The product needs a self-hosted support path (no external helpdesk as source of truth) with: threaded messages, agent workflow, SLA targets, optional outbound/inbound email, response macros, and a small knowledge base. Government operators must manage queues without broad data leaks; parents/drivers must only see their own tickets unless they are agents.
Decision
- Postgres data model (migrated via
ensureSchemainserver.ts):- Extend
support_ticketswithpriority,queue,assigned_user_id, SLA timestamps,last_activity_at. - Add
support_ticket_messages(public + internal),support_ticket_events(audit),support_sla_policies,support_macros,support_outbound_email_jobs,kb_categories,kb_articles.
- Extend
- Authorization — new permissions in
lib/authz.ts/src/authzClient.ts:feedback_support_agent— queue, PATCH ticket, internal notes, SLA policy API.feedback_support_macros_manage— macro CRUD.feedback_kb_admin— KB article CRUD.- Default government_operator receives agent + macros + KB admin; migration
INSERT … ON CONFLICT DO NOTHINGadds these for existing operators.
- API surface (Express in
server.ts):- Ticket detail
GET /api/feedback/support/:id(owner or agent);PATCH(reporter may only reopen/close own ticket);POST …/messages. - Agent queue
GET /api/feedback/support/queue(requires agent +feedback_support_read_all). - SLA CRUD under
/api/feedback/sla-policies. - Macros under
/api/feedback/support/macros. - KB under
/api/kb/*(authenticated read withvisible_rolesfilter). - Inbound email webhook
POST /api/webhooks/support-inboundwith raw JSON body andx-omnitrack-signature= HMAC-SHA256 hex over body usingSUPPORT_EMAIL_INBOUND_SECRET. Payload attributes customer replies to the ticket reporter (staff should use the app).
- Ticket detail
- Outbound email —
SUPPORT_SMTP_URL(orSMTP_URL) via nodemailer; jobs table +processSupportOutboundQueueon a 30s interval after HTTP listen. Attachments: not supported in v1 (plain text only); size caps on body. - No ticket attachments in storage for v1 — documented here to avoid unbounded DB growth and malware surface.
Consequences
- Operators must be granted DB-backed permissions (not only UI);
governmentsuper-user still receives synthetic full permission list inuserJsonForClient. - Scaling outbound email requires a dedicated worker/queue later; the polling loop is acceptable for moderate volume.
PUBLIC_APP_URL/APP_ORIGINimproves email footers when set.
Related
docs/PLATFORM-MASTER.md§5 log..cursor/rules/overheid-operator-ux.mdc,feedback-product-github.mdc.