You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There's no structured way for a user to raise an issue and have it tracked through to resolution. Alerts, compliance cases, and DSAR requests (#371) all have their own well-defined workflows for their specific domains, but a general "my deposit hasn't shown up" or "I have a question about my tax report" has nowhere to go except an ad hoc external channel (email, WhatsApp free text) with no ticket record, no SLA, no assignment, no resolution history the user or support can refer back to. This issue adds a lightweight, general-purpose support ticket system, structurally similar to the compliance-case model (#373) but user-initiated and non-confidential.
Current State
src/compliance/cases.ts (Sanctions Screening Case Management & SAR Workflow #373) — the closest structural precedent: a case model, append-only event timeline, status state machine, assignment, SLA — but that system is compliance-specific, admin-initiated, and deliberately invisible to the user (no tipping-off). A support ticket is the opposite in that last respect: fully visible and collaborative with the user.
src/whatsapp/handler.ts, src/telegram/handler.ts — existing user-facing channels that could optionally create/reply-to tickets, though the primary interface should be the REST API first.
TicketMessage: append-only thread (ticketId, author (user or admin), body, attachmentRefs?, createdAt, internal: boolean for admin-only notes not visible to the user — mirroring the visible/internal distinction that's natural in any support tool).
POST /api/v1/support/tickets (+ optional context auto-attachment: if raised from a specific transaction/report view, the client can pass a reference id so support starts with relevant context already attached, reducing back-and-forth).
GET /api/v1/support/tickets, GET /:id, POST /:id/reply — user-facing thread view/reply.
Admin side: GET /api/v1/admin/support/tickets (queue, filterable by status/category/assignee/SLA), PATCH /:id (status/assignment/priority), POST /:id/reply (internal or user-visible).
SLA timers per priority (config), surfaced in the admin queue (reuse the SLA-breach-flagging pattern already established for compliance cases).
Edge Cases & Failure Modes
Ticket references sensitive financial data: fine — this is the user's own data about their own account, unlike the compliance-case confidentiality requirement; standard owner-scoping applies, not the stricter no-tipping-off rule.
User replies after RESOLVED: automatically reopens to IN_PROGRESS (a resolved-but-not-actually-resolved ticket shouldn't require the user to know to click a separate "reopen" action).
Abusive/spam ticket content: basic rate limiting on creation; no need for heavier moderation infrastructure at this scope.
Ticket about a specific transaction/tax report that's later corrected by a real bug fix: the ticket thread stays as the historical record; no automatic tie-in to a code-deploy tracking system (out of scope).
Internal admin notes accidentally exposed: the internal flag must be enforced server-side on every read path a user hits, not just hidden client-side.
Security & Privacy Considerations
Owner-scoped for the user's own tickets; admin access requires the appropriate admin scope, audit-logged for sensitive actions (status changes, assignment).
internal: true messages are never returned to the user-facing read endpoints, enforced at the query level, not just response filtering (a common class of bug: filtering client-side or too late in the pipeline).
Attachment references (if implemented) should reuse whatever secure-file-upload pattern already exists elsewhere in the codebase, not invent a new one.
Out of Scope
Live chat / real-time support (async ticket threading only, v1).
SLA-based automated escalation to a specific human beyond surfacing the breach (compliance-case-style escalation is heavier machinery than a general support queue needs initially).
Integration with a third-party helpdesk platform (native, in-repo implementation).
Problem Statement
There's no structured way for a user to raise an issue and have it tracked through to resolution. Alerts, compliance cases, and DSAR requests (#371) all have their own well-defined workflows for their specific domains, but a general "my deposit hasn't shown up" or "I have a question about my tax report" has nowhere to go except an ad hoc external channel (email, WhatsApp free text) with no ticket record, no SLA, no assignment, no resolution history the user or support can refer back to. This issue adds a lightweight, general-purpose support ticket system, structurally similar to the compliance-case model (#373) but user-initiated and non-confidential.
Current State
src/compliance/cases.ts(Sanctions Screening Case Management & SAR Workflow #373) — the closest structural precedent: a case model, append-only event timeline, status state machine, assignment, SLA — but that system is compliance-specific, admin-initiated, and deliberately invisible to the user (no tipping-off). A support ticket is the opposite in that last respect: fully visible and collaborative with the user.src/whatsapp/handler.ts,src/telegram/handler.ts— existing user-facing channels that could optionally create/reply-to tickets, though the primary interface should be the REST API first.SupportTicketmodel exists (confirmed).Proposed Solution
SupportTicket:userId,subject,category(ACCOUNT|TRANSACTION|TAX|TECHNICAL|OTHER),status(OPEN→IN_PROGRESS→AWAITING_USER→RESOLVED→CLOSED),priority,assignedTo,createdAt,resolvedAt.TicketMessage: append-only thread (ticketId,author(user or admin),body,attachmentRefs?,createdAt,internal: booleanfor admin-only notes not visible to the user — mirroring the visible/internal distinction that's natural in any support tool).POST /api/v1/support/tickets(+ optional context auto-attachment: if raised from a specific transaction/report view, the client can pass a reference id so support starts with relevant context already attached, reducing back-and-forth).GET /api/v1/support/tickets,GET /:id,POST /:id/reply— user-facing thread view/reply.GET /api/v1/admin/support/tickets(queue, filterable by status/category/assignee/SLA),PATCH /:id(status/assignment/priority),POST /:id/reply(internal or user-visible).Edge Cases & Failure Modes
RESOLVED: automatically reopens toIN_PROGRESS(a resolved-but-not-actually-resolved ticket shouldn't require the user to know to click a separate "reopen" action).internalflag must be enforced server-side on every read path a user hits, not just hidden client-side.Security & Privacy Considerations
internal: truemessages are never returned to the user-facing read endpoints, enforced at the query level, not just response filtering (a common class of bug: filtering client-side or too late in the pipeline).Out of Scope
Suggested Implementation Plan
SupportTicket,TicketMessagemodels + migration/rollback.docs/support doc +docs/openapi.yaml.Acceptance Criteria
TicketMessage.internalnotes are never returned on any user-facing read path, enforced at the query levelRESOLVEDticket automatically reopens it toIN_PROGRESSdocs/openapi.yaml+ a short support-system doc added; unit + integration tests green