Customer Dashboard — Tickets
The Tickets section is a helpdesk: support tickets from every channel land in an inbox, get assigned (manually or by routing rules), tracked against SLA policies, and optionally synced with an external ticketing provider. Sub-sections: Overview (inbox), Settings, Auto-assignment (routing), SLA, Integrations.
Screenshots





Architecture Overview
How It Works (Step-by-Step)
Every route runs four layers (from ticket.routes.ts header): verifyJwt → requireFeatureAccess(tickets module) → requirePermission("tickets","view") → resolveTicketScope → requireTicketPermission(action). Scope is mandatory on every route — without it a customer could read the whole org's tickets.
- Overview / inbox (
/tickets).GET /api/tickets(list,view),GET /api/tickets/stats,GET /api/tickets/:id(detail),GET /api/tickets/:id/thread+/events(conversation + audit timeline). The module-onlyGET /api/tickets/permissionstells the UI which controls to show. - Create & reply.
POST /api/tickets(create),POST /api/tickets/:id/reply+/comment(create),POST /api/tickets/:id/convert-to-lead(turn a ticket into a CRM lead),PATCH /api/tickets/:id(update),PATCH /api/tickets/:id/assign(manager — reassigning across other users' tickets). - Settings (
/tickets/settings).GETwithview;PATCH /api/tickets/settingsis manager-only. Stored in TicketSettings. - Auto-assignment / routing (
/tickets/routing). CRUD on routing rules; readsview, writes manager-only. Rules live in TicketRoutingRule and decide who a new ticket is assigned to. - SLA (
/tickets/sla). CRUD on SLA policies (first-response / resolution targets); readsview, writes manager-only. Stored in TicketSlaPolicy. - Integrations (
/tickets/integrations). Connect an external ticketing provider (and Gmail mailboxes for email ticketing). Requiresmanage+ manager + thetickets.integrationsfeature. Provider config in TicketProviderConfig; mailboxes in TicketMailbox. The provider webhook (ticketProviderWebhook) ingests external events.
File Reference
| File | Role | Key Functions |
|---|---|---|
routes/ticket.routes.ts | Authoritative endpoint/permission table | gate, canView, canCreate, managerCreate |
controllers/ticket.controller.ts | Inbox, replies, settings, routing, SLA, integrations | listTickets, replyToTicket, assignTicket, updateTicketSettings |
controllers/ticketMailbox.controller.ts | Gmail mailboxes for email ticketing | listTicketMailboxes, getTicketMailboxAuthUrl |
middleware/ticketScope.middleware.ts | Tenant + visibility scope + manager check | resolveTicketScope, requireTicketManager |
middleware/ticketPermission.middleware.ts | Per-action authority | requireTicketPermission |
Data Model
| Collection | Why it exists | Key fields |
|---|---|---|
Ticket | The support request | customerUID, status, assignedToUID, priority |
TicketMessage | Thread messages (customer + agent replies) | ticketId, body, direction |
TicketEvent | Audit timeline (status/assign changes) | ticketId, type |
TicketExternalLink | Link to an external provider ticket | provider, external id |
TicketRoutingRule | Auto-assignment rules | conditions, assignToUID |
TicketSlaPolicy | SLA targets | first-response / resolution minutes |
TicketSettings | Per-org ticket config | defaults, channels |
TicketMailbox / TicketProviderConfig | Email inbox + external provider | tokens, provider type |
API Endpoints
List tickets
- Method:
GET· Path:/api/tickets· Auth: gate +requireTicketPermission("view")
Create / reply
POST /api/tickets(create),POST /api/tickets/:id/reply(create),POST /api/tickets/:id/comment(create).
Assign (manager)
- Method:
PATCH· Path:/api/tickets/:id/assign· Auth:canCreate+requireTicketManager.
Settings / routing / SLA (read view, write manager)
GET/PATCH /api/tickets/settings,GET/POST/PATCH/DELETE /api/tickets/routing-rules[/:ruleId],GET/POST/PATCH/DELETE /api/tickets/sla-policies[/:policyId].
Integrations (manage + manager + feature)
GET /api/tickets/integrations,PUT/DELETE /api/tickets/integrations/:provider, mailbox routesGET/PATCH/DELETE /api/tickets/mailboxes[...].
Provider webhook (public, signature-verified)
- Declared in
ticketInternal.routes.ts— the external provider posts events with no JWT.
External Integrations
- External ticketing provider — two-way link via
tickets.integrations; outbound config inTicketProviderConfig, inbound viaticketProviderWebhook. - Gmail (ticket mailboxes) — multi-inbox email ticketing. The OAuth callback is public (browser redirect carries no JWT) and lives in
ticketInternal.routes.ts.
How to Test (End-to-End)
- Grant
tickets(+tickets.view/.create). Open/tickets. - Create a ticket → it appears in the inbox and
GET /api/tickets/statsincrements. - Reply to it → a
TicketMessageis written and shows in the thread; aTicketEventlogs the action. - As a manager, add a routing rule and an SLA policy → confirm new tickets auto-assign and show SLA timers.
- As a non-manager, confirm
PATCH /api/tickets/settingsreturns 403. - With
tickets.integrations, connect a mailbox and confirm inbound email creates tickets.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Tickets section hidden | tickets module off (or crm_leads missing — Tickets depends on Leads) | Grant Tickets + Leads |
| Can see tickets but can't reply | tickets.create not granted | Grant the create permission |
| Settings/routing/SLA return 403 | Not an org manager | Those writes are manager-only |
| Integrations tab locked | tickets.integrations feature off | Grant the integrations capability |
| Inbound email not creating tickets | Mailbox not connected / webhook not firing | Reconnect the mailbox; verify provider webhook |