Skip to content

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 ​

Tickets — inboxTickets — ticket detailTickets — auto-assignment / routingTickets — SLA policiesTickets — integrations

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.

  1. 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-only GET /api/tickets/permissions tells the UI which controls to show.
  2. 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).
  3. Settings (/tickets/settings). GET with view; PATCH /api/tickets/settings is manager-only. Stored in TicketSettings.
  4. Auto-assignment / routing (/tickets/routing). CRUD on routing rules; reads view, writes manager-only. Rules live in TicketRoutingRule and decide who a new ticket is assigned to.
  5. SLA (/tickets/sla). CRUD on SLA policies (first-response / resolution targets); reads view, writes manager-only. Stored in TicketSlaPolicy.
  6. Integrations (/tickets/integrations). Connect an external ticketing provider (and Gmail mailboxes for email ticketing). Requires manage + manager + the tickets.integrations feature. Provider config in TicketProviderConfig; mailboxes in TicketMailbox. The provider webhook (ticketProviderWebhook) ingests external events.

File Reference ​

FileRoleKey Functions
routes/ticket.routes.tsAuthoritative endpoint/permission tablegate, canView, canCreate, managerCreate
controllers/ticket.controller.tsInbox, replies, settings, routing, SLA, integrationslistTickets, replyToTicket, assignTicket, updateTicketSettings
controllers/ticketMailbox.controller.tsGmail mailboxes for email ticketinglistTicketMailboxes, getTicketMailboxAuthUrl
middleware/ticketScope.middleware.tsTenant + visibility scope + manager checkresolveTicketScope, requireTicketManager
middleware/ticketPermission.middleware.tsPer-action authorityrequireTicketPermission

Data Model ​

CollectionWhy it existsKey fields
TicketThe support requestcustomerUID, status, assignedToUID, priority
TicketMessageThread messages (customer + agent replies)ticketId, body, direction
TicketEventAudit timeline (status/assign changes)ticketId, type
TicketExternalLinkLink to an external provider ticketprovider, external id
TicketRoutingRuleAuto-assignment rulesconditions, assignToUID
TicketSlaPolicySLA targetsfirst-response / resolution minutes
TicketSettingsPer-org ticket configdefaults, channels
TicketMailbox / TicketProviderConfigEmail inbox + external providertokens, 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 routes GET/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 in TicketProviderConfig, inbound via ticketProviderWebhook.
  • 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) ​

  1. Grant tickets (+ tickets.view / .create). Open /tickets.
  2. Create a ticket → it appears in the inbox and GET /api/tickets/stats increments.
  3. Reply to it → a TicketMessage is written and shows in the thread; a TicketEvent logs the action.
  4. As a manager, add a routing rule and an SLA policy → confirm new tickets auto-assign and show SLA timers.
  5. As a non-manager, confirm PATCH /api/tickets/settings returns 403.
  6. With tickets.integrations, connect a mailbox and confirm inbound email creates tickets.

Troubleshooting ​

SymptomCauseFix
Tickets section hiddentickets module off (or crm_leads missing — Tickets depends on Leads)Grant Tickets + Leads
Can see tickets but can't replytickets.create not grantedGrant the create permission
Settings/routing/SLA return 403Not an org managerThose writes are manager-only
Integrations tab lockedtickets.integrations feature offGrant the integrations capability
Inbound email not creating ticketsMailbox not connected / webhook not firingReconnect the mailbox; verify provider webhook

Last updated: