Support Module Backend Documentation
Backend architecture for support inquiries, support requests, feedback, realtime, and maintenance.
Support Module - Backend Documentation
1. Backend Scope and Boundaries
The support backend owns:
- public/customer support inquiry lifecycle
- guest inquiry token lifecycle
- support inquiry message persistence
- inquiry email capture and verification handoff
- admin inquiry handling and conversion to support requests
- authenticated mobile support request lifecycle
- support request message persistence
- admin support request handling
- authenticated mobile feedback submission
- admin feedback review
- support websocket room authorization
- support realtime event publishing and replay
- scheduled inactive inquiry close
The module does not own:
- customer account registration
- customer password/login flows
- email template rendering internals
- global websocket connection management
- generic role/permission storage
- generic activity log persistence
Those responsibilities are delegated to existing auth, notifications, realtime, permissions, and activity services.
2. Module Composition (Aggregate + Leaf)
SupportModule composes:
SupportSharedModuleSupportAdminAggregateModuleSupportInquiriesCustomerModule
SupportAdminAggregateModule composes:
SupportRequestsAdminModuleSupportInquiriesAdminModuleFeedbackAdminModule
Mobile support surfaces are composed centrally by MobileModule:
SupportRequestsMobileModuleFeedbackMobileModule
Shared providers:
| Provider | Responsibility |
|---|---|
SupportRequestDomainService | shared support request reads, mapping, status update helpers, message author mapping |
SupportInquiryDomainService | inquiry create/read/write/identify/verify/link/close behavior |
SupportNotificationService | support request notification handoff |
SupportRealtimePublisherService | support websocket envelope creation and room publishing |
SupportRealtimeAccessService | websocket room authorization and replay sync access |
SupportInquiriesMaintenanceScheduler | cron registration for inactive inquiry closing |
Admin shared providers:
| Provider | Responsibility |
|---|---|
SupportAdminSharedService | admin lookup, audit context extraction, activity recorder access |
3. Data Model (Drizzle / PostgreSQL)
Primary support tables:
support_inquiriessupport_inquiry_messagessupport_inquiry_tokenssupport_requestssupport_request_messagesfeedback_submissions
Schema source of truth:
packages/db/src/schema/support/support-inquiries.tspackages/db/src/schema/support/support-inquiry-messages.tspackages/db/src/schema/support/support-inquiry-tokens.tspackages/db/src/schema/support/support-requests.tspackages/db/src/schema/support/support-request-messages.tspackages/db/src/schema/support/feedback-submissions.ts
Migration source of truth for the new inquiry tables:
packages/db/src/migrations/0002_add_support_inquiries.sql
4. Table Details
4.1 support_inquiries
Purpose:
- stores public/customer inquiry metadata and lifecycle
- stores public tracking code
- links optional customer/admin/support request references
- stores guest contact fields
Important columns:
| Column | Type | Notes |
|---|---|---|
id | serial PK | internal database identity |
tracking_code | varchar(32), unique | customer-safe inquiry reference |
customer_id | uuid nullable FK | linked customer after auth/verification |
guest_name | varchar(255) nullable | optional visitor contact field |
guest_email | varchar(320) nullable | normalized lowercase email |
guest_phone | varchar(32) nullable | optional visitor contact field |
email_verified_at | timestamp nullable | set only when guest email verified |
category | varchar(32) | account, payment, technical, product, order, other |
subject | varchar(255) | non-blank inquiry subject |
status | varchar(32) | open, active, waiting, linked, resolved, closed, spam |
assigned_admin_id | uuid nullable FK | assigned admin user |
support_request_id | integer nullable FK | linked support request |
last_visitor_message_at | timestamp nullable | latest guest/customer message |
last_admin_message_at | timestamp nullable | latest admin reply |
closed_at | timestamp nullable | required for closed and spam |
created_at | timestamp | creation time |
updated_at | timestamp | updated on mutation |
Indexes:
- unique
support_inquiries_tracking_code_unique support_inquiries_customer_id_idxsupport_inquiries_guest_email_idxsupport_inquiries_status_idxsupport_inquiries_category_idxsupport_inquiries_assigned_admin_id_idxsupport_inquiries_support_request_id_idxsupport_inquiries_created_at_idx
Check constraints:
- tracking code must be non-blank
- subject must be non-blank
- category must be one of the declared category values
- status must be one of the declared status values
closed_atmust matchclosed/spamstateemail_verified_atrequiresguest_email
4.2 support_inquiry_messages
Purpose:
- stores full inquiry message history
- supports guest, customer, admin, and system authors
Important columns:
| Column | Type | Notes |
|---|---|---|
id | serial PK | internal message identity |
support_inquiry_id | integer FK cascade | parent inquiry |
author_type | varchar(32) | guest, customer, admin, system |
author_customer_id | uuid nullable FK | required only for customer author |
author_admin_id | uuid nullable FK | required only for admin author |
body | text | non-blank message body |
created_at | timestamp | append time |
Indexes:
support_inquiry_messages_support_inquiry_id_idxsupport_inquiry_messages_author_customer_id_idxsupport_inquiry_messages_author_admin_id_idxsupport_inquiry_messages_created_at_idx
Check constraints:
- author type must be valid
- body must be non-blank
- author FK consistency is enforced by author type
4.3 support_inquiry_tokens
Purpose:
- stores hashed guest access tokens for inquiry-specific REST and websocket access
Important columns:
| Column | Type | Notes |
|---|---|---|
id | serial PK | internal token identity |
support_inquiry_id | integer FK cascade | parent inquiry |
token_hash | varchar(64), unique | SHA-256 hex digest of si_... token |
expires_at | timestamp | token expiry |
revoked_at | timestamp nullable | explicit revocation |
created_at | timestamp | creation time |
Indexes:
support_inquiry_tokens_support_inquiry_id_idx- unique
support_inquiry_tokens_token_hash_unique support_inquiry_tokens_expires_at_idx
Check constraints:
- token hash length is exactly 64
expires_atmust be aftercreated_at
4.4 support_requests
Purpose:
- stores authenticated customer support tickets
- existing ticket model used by mobile and admin support request APIs
Important columns:
| Column | Type | Notes |
|---|---|---|
id | serial PK | internal ticket identity |
customer_id | uuid FK cascade | required customer owner |
category | varchar(32) | account, payment, technical, other |
subject | varchar(255) | non-blank ticket subject |
status | varchar(32) | open, in_progress, resolved, closed |
assigned_admin_id | uuid nullable FK | assigned admin |
resolution_note | text nullable | allowed only for resolved/closed |
resolved_at | timestamp nullable | status-coupled |
closed_at | timestamp nullable | status-coupled |
last_customer_message_at | timestamp nullable | latest customer message |
last_admin_message_at | timestamp nullable | latest admin reply |
created_at | timestamp | creation time |
updated_at | timestamp | updated on mutation |
Indexes:
support_requests_customer_id_idxsupport_requests_status_idxsupport_requests_assigned_admin_id_idxsupport_requests_created_at_idxsupport_requests_category_idx
4.5 support_request_messages
Purpose:
- stores full ticket message history
- supports customer and admin authors
Important columns:
| Column | Type | Notes |
|---|---|---|
id | serial PK | internal message identity |
support_request_id | integer FK cascade | parent request |
author_type | varchar(32) | customer or admin |
author_customer_id | uuid nullable FK | required for customer author |
author_admin_id | uuid nullable FK | required for admin author |
body | text | non-blank message body |
created_at | timestamp | append time |
4.6 feedback_submissions
Purpose:
- stores authenticated mobile feedback for admin review
Important columns:
| Column | Type | Notes |
|---|---|---|
id | serial PK | internal feedback identity |
customer_id | uuid FK cascade | submitting customer |
category | varchar(32) | bug_report, feature_request, general_feedback |
subject | varchar(255) | non-blank subject |
message | text | non-blank feedback message |
status | varchar(32) | new, reviewed |
reviewed_by_admin_id | uuid nullable FK | admin reviewer |
reviewed_at | timestamp nullable | review time |
created_at | timestamp | creation time |
updated_at | timestamp | updated on mutation |
Check constraints enforce valid category/status, non-blank subject/message, and review state consistency.
5. Runtime Rules and Domain Invariants
5.1 Support inquiry creation
Create flow:
- Normalize category, subject, message, and guest contact fields.
- Generate unique
trackingCode. - If authenticated customer exists, set
customerId. - If guest, generate
si_...token and store SHA-256 hash. - Insert inquiry.
- Insert system greeting message.
- Insert optional initial guest/customer message when provided.
- Return detail response and
inquiryAccessTokenonly for guest-created inquiries.
Transaction boundary:
- inquiry row
- token row
- system greeting message
- optional initial visitor message
5.2 Inquiry message writes
Visitor messages:
- accepted from customer JWT or valid guest inquiry token
- blocked for
closedandspam - update
lastVisitorMessageAt - can move status to active depending on current state
- emit
support.inquiry_message.created
Admin messages:
- require
SupportInquiries_UPDATE - blocked for closed/spam inquiries
- update
lastAdminMessageAt - emit
support.inquiry_message.created - record admin activity
5.3 Inquiry identification
PATCH /api/support-inquiries/:id/identify updates:
guestNameguestEmailguestPhone
Email behavior:
- email is trimmed and lowercased
- empty values are omitted by DTO transform/validation behavior
- a stored email does not imply customer identity
- customer linking requires verification or login
5.4 Inquiry email verification
Request behavior:
- validates the visitor has access to the inquiry
- requires
guestEmail - creates an OTP using verification token infrastructure
- enqueues email via notifications queue
- returns a generic response
Confirm behavior:
- validates OTP
- sets
emailVerifiedAt - silently links
customerIdonly when the verified email matches a customer - never exposes "email exists" versus "email does not exist"
5.5 Inquiry to support request linking
Admin linking behavior:
- If
supportRequestIdis supplied, backend validates target request exists and belongs to the same verified customer. - If
supportRequestIdis omitted, backend creates a newsupport_request. - A new support request can only be created when the inquiry has
customerId. - Non-system inquiry messages are copied into
support_request_messages. - Inquiry
supportRequestIdis set. - Inquiry status becomes
linked.
Category mapping:
| Inquiry category | Support request category |
|---|---|
account | account |
payment | payment |
technical | technical |
product | other |
order | other |
other | other |
5.6 Support request creation
Mobile support request create flow:
- Customer submits category, subject, and initial message.
- Backend creates
support_request. - Backend creates initial
support_request_message. - Response includes request detail and message history.
Support requests always require a customer. They are not guest records.
5.7 Support request replies
Customer replies:
- require customer JWT and ownership
- blocked when status is
closed - reopen
resolvedrequests toopen - update
lastCustomerMessageAt - emit
support.request_message.created
Admin replies:
- require
SupportRequests_UPDATE - blocked when status is
closed - update
lastAdminMessageAt - emit
support.request_message.created - notify customer via
SupportNotificationService - record admin activity
5.8 Feedback review
Feedback create:
- requires customer JWT
- inserts new
feedback_submissionsrow - status defaults to
new
Feedback review:
- requires
Feedback_UPDATE - sets
status = reviewed - sets
reviewedByAdminId - sets
reviewedAt - records admin activity
6. Authentication and Authorization
6.1 REST guards
| Surface | Guards |
|---|---|
| Public/customer inquiries | SupportInquiryOptionalAuthGuard, selected routes also IpThrottlerGuard |
| Admin inquiries | JwtAuthGuard, AdminActorGuard, RoleGuard |
| Mobile support requests | JwtAuthGuard |
| Admin support requests | JwtAuthGuard, AdminActorGuard, RoleGuard |
| Mobile feedback | JwtAuthGuard |
| Admin feedback | JwtAuthGuard, AdminActorGuard, RoleGuard |
6.2 Permissions
Permission modules:
SupportInquiriesSupportRequestsFeedback
Permission codes:
SupportInquiries_READSupportInquiries_UPDATESupportRequests_READSupportRequests_UPDATEFeedback_READFeedback_UPDATE
6.3 Websocket authorization
The realtime auth guard supports:
- admin JWT
- customer JWT
- guest inquiry token (
si_...)
Room access rules:
| Room | Admin | Customer | Guest |
|---|---|---|---|
| Support request messages | Any request with permission | Own customer request only | Not allowed |
| Support inquiry messages | Any inquiry with permission | Own linked inquiry only | Inquiry matching valid token only |
7. Realtime Architecture
Support realtime is built on the shared RealtimeGateway.
Support contracts live in:
apps/api/src/modules/support/realtime/contracts/support-realtime.contract.ts
Support services:
SupportRealtimePublisherServiceSupportRealtimeAccessService
Room names:
support:request:{supportRequestId}:messagessupport:inquiry:{supportInquiryId}:messages
Client events:
support:join_request_messagessupport:leave_request_messagessupport:sync_request_messagessupport:join_inquiry_messagessupport:leave_inquiry_messagessupport:sync_inquiry_messages
Server event types:
support.request_message.createdsupport.inquiry_message.created
Event envelope:
interface SupportRealtimeEnvelope<TData> {
eventId: string;
eventType: "support.request_message.created" | "support.inquiry_message.created";
occurredAt: string;
data: TData;
}Event IDs use UUIDv7, which preserves ordering properties for replay and follows the cross-system ID policy.
8. Maintenance Scheduler
Scheduler source:
apps/api/src/modules/support/shared/schedulers/support-inquiries-maintenance.scheduler.ts
Runtime config:
| Variable | Default | Description |
|---|---|---|
SUPPORT_INQUIRIES_INACTIVE_CLOSE_CRON | 0 * * * * | Cron expression for inactive inquiry sweep |
SUPPORT_INQUIRIES_INACTIVE_CLOSE_DAYS | 7 | Number of inactive days before close |
The scheduler:
- Resolves env-backed cron expression and inactivity window at startup.
- Registers a cron job with
SchedulerRegistry. - Calls
SupportInquiryDomainService.closeInactive(before). - Marks stale
open,active,waiting, andresolvedinquiries asclosed. - Sets
closedAtandupdatedAt. - Logs success/failure using Nest
Logger.
It does not delete rows. Message history remains available for admins.
9. Email and Notification Integration
Support inquiry email verification uses:
VerificationTokenServiceBullServiceQueueName.NOTIFICATIONSNotificationJob.SEND_EMAIL
Verification request flow:
Support request admin replies use:
SupportNotificationService.notifyCustomerOfAdminReply(...)- realtime event publishing
- activity recorder
10. Activity Logging
Admin support request operations record:
- status updates
- assignment changes
- replies
Admin support inquiry operations record:
- replies
- assignment changes
- status updates
- link/create support request operations
Admin feedback operations record:
- review action
Activity fields include:
- admin user id/name/role
- action
- module
- resource id/type
- request context from
SupportAdminSharedService.buildAuditContext(...) - changes when applicable
11. Error and Resilience Contracts
Inquiry-specific error codes:
| HTTP | errorCode | Scenario |
|---|---|---|
| 404 | SUPPORT_INQUIRY_NOT_FOUND | Inquiry does not exist |
| 403 | SUPPORT_INQUIRY_ACCESS_DENIED | Customer/guest lacks access |
| 403 | SUPPORT_INQUIRY_TOKEN_INVALID | Missing, expired, revoked, or mismatched guest token |
| 400 | SUPPORT_INQUIRY_CLOSED | Message attempted against closed/spam inquiry |
| 400 | SUPPORT_INQUIRY_LINK_INVALID | Invalid support request link target or identity mismatch |
| 400 | SUPPORT_INQUIRY_EMAIL_VERIFICATION_INVALID | Invalid/missing verification state or OTP |
| 400 | SUPPORT_INQUIRY_SUPPORT_REQUEST_REQUIRED | Support request creation requires verified customer |
Existing support request and feedback services also use standard Nest exceptions and global exception formatting.
Resilience controls:
- token hashes are stored, not raw guest tokens
- email lookup is silent
- multi-row create/link operations are transactional
- websocket sync supports replay and gap detection
- inactive close is idempotent
- admin activity record failures are handled by the activity service without blocking primary operations
12. Query, Pagination, and Mapping Strategy
List endpoints use the project QueryDto and PaginationUtil patterns:
- normalize pagination defaults
- apply Drizzle
limitandoffsetthrough utility output - count total rows only for paginated responses
- map DB rows to response DTOs in services/domain services
Mapping boundaries:
- controllers return
ResponseDto<T> - services/domain services map raw database rows
- guest token hashes are never mapped to response DTOs
- admin response DTOs include internal references needed for operations
- mobile/customer response DTOs omit admin-only details where the existing request DTOs already enforce that behavior
13. Backend Diagram
14. Conversion Data Flow
15. Release/QA Checklist
- DB migration for
support_inquiries,support_inquiry_messages, andsupport_inquiry_tokenshas run before deploying API code. - Permission seed includes
SupportInquiries. - Admin role has
SupportInquiries_READandSupportInquiries_UPDATEwhere needed. - Guest token storage hashes raw
si_...tokens. - Guest token is returned only on create and only for guest-created inquiries.
- Customer inquiry create does not return
inquiryAccessToken. - Email verification request returns generic response for all email states.
- OTP confirm links existing customer only after valid verification.
- Inquiry-to-request conversion requires
customerId. - Closed/spam inquiry messages are blocked.
- Existing support request reply behavior still blocks closed tickets and reopens resolved tickets.
- Feedback review sets reviewer metadata and activity record.
- Websocket room joins enforce correct customer/admin/guest access.
- Realtime sync fallback behavior is documented for frontend.
- Inactive-close scheduler starts with env-backed config.
16. File Map
Support aggregate and shared:
apps/api/src/modules/support/support.module.tsapps/api/src/modules/support/shared/support-shared.module.tsapps/api/src/modules/support/shared/services/support-inquiry-domain.service.tsapps/api/src/modules/support/shared/services/support-request-domain.service.tsapps/api/src/modules/support/shared/services/support-notification.service.tsapps/api/src/modules/support/shared/schedulers/support-inquiries-maintenance.scheduler.ts
Support inquiries:
apps/api/src/modules/support/customer/inquiries/*apps/api/src/modules/support/admin/inquiries/*
Support requests:
apps/api/src/modules/support/mobile/support-requests/*apps/api/src/modules/support/admin/support-requests/*
Feedback:
apps/api/src/modules/support/mobile/feedback/*apps/api/src/modules/support/admin/feedback/*
Realtime:
apps/api/src/modules/support/realtime/*apps/api/src/services/realtime/realtime.gateway.tsapps/api/src/services/realtime/realtime-auth.guard.ts
Schema:
packages/db/src/schema/support/*
Migration:
packages/db/src/migrations/0002_add_support_inquiries.sql
17. Environment Variables
| Variable | Default | Description |
|---|---|---|
SUPPORT_EMAIL | optional | Support contact email used by email templates/config where applicable |
SUPPORT_INQUIRIES_INACTIVE_CLOSE_CRON | 0 * * * * | Cron schedule for closing inactive inquiries |
SUPPORT_INQUIRIES_INACTIVE_CLOSE_DAYS | 7 | Inactivity window before inquiry auto-close |
OTP_EXPIRY_MINUTES | 15 | OTP expiry used by verification token service |
OTP_MAX_ATTEMPTS | 5 | Maximum OTP attempts |
WS_NAMESPACE | /realtime | Websocket namespace |
EVENT_HISTORY_ENABLED | true | Enables event replay history |
EVENT_HISTORY_TTL | 3600 | Realtime replay TTL in seconds |
ACK_TIMEOUT | 10000 | Socket acknowledgement timeout |
ACK_RETRIES | 0 | Socket acknowledgement retries |
WS_AUTH_DISABLED | false | Realtime auth bypass for non-prod/test only |
Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.
See Also
- Feature Guide: See Support - Feature List for user-facing capabilities and workflows.
- API Reference: See Support - API & Integration Guide for endpoint, payload, and websocket contracts.