تخطي إلى المحتوى الرئيسي

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

  1. Postgres data model (migrated via ensureSchema in server.ts):
    • Extend support_tickets with priority, 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.
  2. 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 NOTHING adds these for existing operators.
  3. 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 with visible_roles filter).
    • Inbound email webhook POST /api/webhooks/support-inbound with raw JSON body and x-omnitrack-signature = HMAC-SHA256 hex over body using SUPPORT_EMAIL_INBOUND_SECRET. Payload attributes customer replies to the ticket reporter (staff should use the app).
  4. Outbound emailSUPPORT_SMTP_URL (or SMTP_URL) via nodemailer; jobs table + processSupportOutboundQueue on a 30s interval after HTTP listen. Message bodies stay plain text with server size caps.
  5. Ticket attachmentssupport_ticket_attachments stores up to 3 files per ticket (max 4 MB each) as BYTEA in Postgres; MIME allowlist JPEG, PNG, WebP, PDF. POST /api/feedback/support accepts optional JSON attachments[] with base64; download via authenticated GET /api/feedback/support/:ticketId/attachments/:attachmentId/file (reporter or agent). For multi-tenant scale, plan object storage and signed URLs instead of large BYTEA hot rows.
  6. Reporter vs agent UI — Everyone with feedback_support_create uses the Support nav tab to submit tickets (optional attachments, own ticket list, Help center/KB). Users with feedback_support_agent and feedback_support_read_all also get sidebar Ticket desk for queue filters, full ticket table, macros, SLA summary, and GitHub export so handling stays on that dashboard.

Consequences

  • Operators must be granted DB-backed permissions (not only UI); government super-user still receives synthetic full permission list in userJsonForClient.
  • Scaling outbound email requires a dedicated worker/queue later; the polling loop is acceptable for moderate volume.
  • PUBLIC_APP_URL / APP_ORIGIN improves email footers when set.
  • docs/PLATFORM-MASTER.md §5 log.
  • .cursor/rules/overheid-operator-ux.mdc, feedback-product-github.mdc.