Support Module API & Integration Guide
REST and websocket contracts for support inquiries, support requests, feedback, and realtime support.
Audience: Web/mobile/admin frontend developers Scope: Support inquiries, support requests, feedback, realtime chat, email verification, and admin conversion flows
Support Module - API & Integration Guide
1. How to Read / Quick Metadata
- Module:
Support - Response envelope: successful endpoints return
ResponseDto<T> - Primary REST base URLs:
- Public/customer inquiries:
/api/support-inquiries - Admin inquiries:
/api/admin/support-inquiries - Mobile support requests:
/api/mobile/support-requests - Admin support requests:
/api/admin/support-requests - Mobile feedback:
/api/mobile/feedback - Admin feedback:
/api/admin/feedback
- Public/customer inquiries:
- Primary websocket namespace: configured by
WS_NAMESPACE, default/realtime - Guest inquiry auth:
Authorization: Bearer si_... - Customer/admin auth:
Authorization: Bearer <jwt>
2. High-Level Overview
Support API is split into:
- public/customer inquiry endpoints for homepage live chat and inquiry capture
- admin inquiry endpoints for staff handling and support ticket conversion
- mobile support request endpoints for authenticated customer tickets
- admin support request endpoints for staff ticket handling
- mobile feedback endpoint for one-way feedback submission
- admin feedback endpoints for review workflows
- websocket events for support request and support inquiry message deltas
REST remains the source of truth for full state. Websocket is the live delta and replay layer.
3. Auth Models
| Surface | Auth |
|---|---|
POST /api/support-inquiries | Optional customer JWT or guest |
GET /api/support-inquiries | Customer JWT |
GET/POST/PATCH /api/support-inquiries/:id... | Customer JWT or guest si_... token |
/api/admin/support-inquiries | Admin JWT + admin actor + role permission |
/api/mobile/support-requests | Customer JWT |
/api/admin/support-requests | Admin JWT + admin actor + role permission |
/api/mobile/feedback | Customer JWT |
/api/admin/feedback | Admin JWT + admin actor + role permission |
| support request websocket room | Admin JWT or owning customer JWT |
| support inquiry websocket room | Admin JWT, owning customer JWT, or matching guest si_... token |
4. Core Concepts and Terminology
- support inquiry: guest/customer chat-style record with tracking code and optional customer link
- inquiry access token: raw
si_...bearer token returned only to guests on inquiry creation - tracking code: public inquiry reference such as
INQ-8K4P2X - system greeting: automatic first inquiry message created by the backend
- email verification: OTP flow that allows safe customer linking after a guest provides email
- support request: authenticated customer support ticket
- feedback submission: one-way customer feedback item for admin review
- room: websocket subscription target for support message events
- event envelope: realtime payload wrapper containing
eventId,eventType,occurredAt, anddata
5. Route Summary
5.1 Public / Customer Support Inquiries
| Method | Path | Auth |
|---|---|---|
POST | /api/support-inquiries | Optional customer JWT or guest |
GET | /api/support-inquiries | Customer JWT |
GET | /api/support-inquiries/:id | Customer JWT or guest inquiry token |
GET | /api/support-inquiries/:id/messages | Customer JWT or guest inquiry token |
POST | /api/support-inquiries/:id/messages | Customer JWT or guest inquiry token |
PATCH | /api/support-inquiries/:id/identify | Customer JWT or guest inquiry token |
POST | /api/support-inquiries/:id/verify-email/request | Customer JWT or guest inquiry token |
POST | /api/support-inquiries/:id/verify-email/confirm | Customer JWT or guest inquiry token |
5.2 Admin Support Inquiries
| Method | Path | Permission |
|---|---|---|
GET | /api/admin/support-inquiries | SupportInquiries_READ |
GET | /api/admin/support-inquiries/:id | SupportInquiries_READ |
POST | /api/admin/support-inquiries/:id/messages | SupportInquiries_UPDATE |
PATCH | /api/admin/support-inquiries/:id/assign | SupportInquiries_UPDATE |
PATCH | /api/admin/support-inquiries/:id/status | SupportInquiries_UPDATE |
POST | /api/admin/support-inquiries/:id/link-support-request | SupportInquiries_UPDATE |
5.3 Mobile Support Requests
| Method | Path | Auth |
|---|---|---|
POST | /api/mobile/support-requests | Customer JWT |
GET | /api/mobile/support-requests | Customer JWT |
GET | /api/mobile/support-requests/:id | Customer JWT |
POST | /api/mobile/support-requests/:id/messages | Customer JWT |
5.4 Admin Support Requests
| Method | Path | Permission |
|---|---|---|
GET | /api/admin/support-requests | SupportRequests_READ |
GET | /api/admin/support-requests/:id | SupportRequests_READ |
PATCH | /api/admin/support-requests/:id/status | SupportRequests_UPDATE |
PATCH | /api/admin/support-requests/:id/assign | SupportRequests_UPDATE |
POST | /api/admin/support-requests/:id/messages | SupportRequests_UPDATE |
5.5 Feedback
| Method | Path | Auth / Permission |
|---|---|---|
POST | /api/mobile/feedback | Customer JWT |
GET | /api/admin/feedback | Feedback_READ |
GET | /api/admin/feedback/:id | Feedback_READ |
PATCH | /api/admin/feedback/:id/review | Feedback_UPDATE |
6. Support Inquiry Endpoint Details
6.1 Create inquiry
| Aspect | Details |
|---|---|
| Endpoint | POST /api/support-inquiries |
| Auth | Optional customer JWT or guest |
| Body DTO | CreateSupportInquiryDto |
| Success DTO | CreateSupportInquiryResponseDto |
| Rate limit | IpThrottlerGuard |
Request body:
{
"category": "product",
"subject": "Question about a thangka painting",
"message": "Is this piece available in a larger size?",
"guestName": "Alex Customer",
"guestEmail": "alex@example.com",
"guestPhone": "+9779800000000"
}Response body for guest-created inquiry:
{
"message": "Support inquiry created successfully",
"data": {
"id": 10,
"trackingCode": "INQ-8K4P2X",
"customerId": null,
"guestEmail": "alex@example.com",
"category": "product",
"subject": "Question about a thangka painting",
"status": "open",
"assignedAdminId": null,
"supportRequestId": null,
"lastVisitorMessageAt": "2026-06-04T10:01:00.000Z",
"lastAdminMessageAt": null,
"createdAt": "2026-06-04T10:00:00.000Z",
"updatedAt": "2026-06-04T10:01:00.000Z",
"guestName": "Alex Customer",
"guestPhone": "+9779800000000",
"emailVerifiedAt": null,
"closedAt": null,
"messages": [
{
"id": 100,
"supportInquiryId": 10,
"authorType": "system",
"authorCustomerId": null,
"authorAdminId": null,
"authorName": "System",
"authorImage": null,
"body": "Hi, how can we help? You can leave your email so we can follow up.",
"createdAt": "2026-06-04T10:00:00.000Z"
},
{
"id": 101,
"supportInquiryId": 10,
"authorType": "guest",
"authorCustomerId": null,
"authorAdminId": null,
"authorName": "Alex Customer",
"authorImage": null,
"body": "Is this piece available in a larger size?",
"createdAt": "2026-06-04T10:01:00.000Z"
}
],
"inquiryAccessToken": "si_9f5e1c25b0f4475db9f17be7a8c3a123"
}
}Authenticated customer behavior:
- request uses customer JWT
customerIdis setinquiryAccessTokenis not returned
6.2 List current customer's inquiries
| Aspect | Details |
|---|---|
| Endpoint | GET /api/support-inquiries |
| Auth | Customer JWT |
| Query DTO | QuerySupportInquiriesDto |
| Success DTO | SupportInquiryListItemResponseDto[] paginated |
Query params:
| Param | Type | Default | Notes |
|---|---|---|---|
page | number | 1 | inherited from QueryDto |
size | number | 20 | inherited from QueryDto |
pagination | boolean | true | set false for unpaginated |
status | inquiry status | optional | filters status |
category | inquiry category | optional | filters category |
sort | createdAt or updatedAt | updatedAt | sort field |
order | asc or desc | desc | inherited from QueryDto |
Guest users do not list inquiries. Guests fetch detail/messages by id with Bearer si_....
6.3 Get inquiry detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/support-inquiries/:id |
| Auth | Customer JWT or guest inquiry token |
| Params | id integer |
| Success DTO | SupportInquiryDetailResponseDto |
Guest header:
Authorization: Bearer si_9f5e1c25b0f4475db9f17be7a8c3a123Access rules:
- customer can access only inquiries linked to their
customerId - guest token can access only its own inquiry
- admin uses admin inquiry endpoints instead
6.4 Get inquiry messages
| Aspect | Details |
|---|---|
| Endpoint | GET /api/support-inquiries/:id/messages |
| Auth | Customer JWT or guest inquiry token |
| Success DTO | SupportInquiryMessageResponseDto[] |
Messages are returned in chronological order.
6.5 Add inquiry message
| Aspect | Details |
|---|---|
| Endpoint | POST /api/support-inquiries/:id/messages |
| Auth | Customer JWT or guest inquiry token |
| Body DTO | CreateSupportInquiryMessageDto |
| Success DTO | SupportInquiryDetailResponseDto |
| Rate limit | IpThrottlerGuard |
Request body:
{
"body": "I would like to know shipping time to Kathmandu."
}Rules:
bodyis trimmed and must be non-blank- closed/spam inquiries reject new messages
- write emits
support.inquiry_message.created
6.6 Identify guest contact
| Aspect | Details |
|---|---|
| Endpoint | PATCH /api/support-inquiries/:id/identify |
| Auth | Customer JWT or guest inquiry token |
| Body DTO | IdentifySupportInquiryDto |
| Success DTO | SupportInquiryDetailResponseDto |
| Rate limit | IpThrottlerGuard |
Request body:
{
"guestName": "Alex Customer",
"guestEmail": "alex@example.com",
"guestPhone": "+9779800000000"
}Rules:
- email is normalized to lowercase
- existing customer match is not disclosed
- identity is not linked until verification succeeds or user logs in
6.7 Request email verification
| Aspect | Details |
|---|---|
| Endpoint | POST /api/support-inquiries/:id/verify-email/request |
| Auth | Customer JWT or guest inquiry token |
| Body | none |
| Success DTO | generic response |
| Rate limit | IpThrottlerGuard |
Success response:
{
"message": "Support inquiry verification code sent",
"data": {
"sent": true
}
}Security behavior:
- response does not reveal whether email exists in customer table
- frontend should show "If this email can receive messages, a code has been sent"
6.8 Confirm email verification
| Aspect | Details |
|---|---|
| Endpoint | POST /api/support-inquiries/:id/verify-email/confirm |
| Auth | Customer JWT or guest inquiry token |
| Body DTO | ConfirmSupportInquiryEmailDto |
| Success DTO | SupportInquiryDetailResponseDto |
| Rate limit | IpThrottlerGuard |
Request body:
{
"otp": "123456"
}Rules:
- OTP length must be 4 to 12 characters
- successful verification sets
emailVerifiedAt - if verified email belongs to a customer, inquiry links to that
customerId - if email does not belong to a customer, inquiry remains a verified guest contact
7. Admin Inquiry Endpoint Details
7.1 Admin list inquiries
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/support-inquiries |
| Auth | Admin JWT |
| Permission | SupportInquiries_READ |
| Query DTO | QuerySupportInquiriesAdminDto |
| Success DTO | SupportInquiryListItemResponseDto[] paginated |
Query params:
| Param | Type | Default | Notes |
|---|---|---|---|
page | number | 1 | inherited from QueryDto |
size | number | 20 | inherited from QueryDto |
pagination | boolean | true | can be false |
status | inquiry status | optional | filters by status |
category | inquiry category | optional | filters by category |
customerId | uuid | optional | filters linked customer |
assignedAdminId | uuid | optional | filters assignee |
supportRequestId | integer | optional | filters linked ticket |
search | string | optional | tracking code, subject, guest contact |
sort | createdAt or updatedAt | updatedAt | sort field |
order | asc or desc | desc | inherited from QueryDto |
Search examples:
| Request | Notes |
|---|---|
GET /api/admin/support-inquiries?search=INQ-8K4P2X | tracking code lookup |
GET /api/admin/support-inquiries?search=alex@example.com | guest email lookup |
GET /api/admin/support-inquiries?status=open&category=product | active triage |
GET /api/admin/support-inquiries?supportRequestId=24 | linked inquiry lookup |
7.2 Admin get inquiry detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/support-inquiries/:id |
| Permission | SupportInquiries_READ |
| Success DTO | SupportInquiryDetailResponseDto |
Admin detail includes:
- guest contact fields
- linked
customerId - linked
supportRequestId - assigned admin id
- full chronological message history
7.3 Admin add inquiry reply
| Aspect | Details |
|---|---|
| Endpoint | POST /api/admin/support-inquiries/:id/messages |
| Permission | SupportInquiries_UPDATE |
| Body DTO | CreateAdminSupportInquiryMessageDto |
| Success DTO | SupportInquiryDetailResponseDto |
Request body:
{
"body": "Thanks, we are checking availability and will update you shortly."
}Side effects:
- writes admin message
- updates
lastAdminMessageAt - emits
support.inquiry_message.created - records admin activity
7.4 Admin assign inquiry
| Aspect | Details |
|---|---|
| Endpoint | PATCH /api/admin/support-inquiries/:id/assign |
| Permission | SupportInquiries_UPDATE |
| Body DTO | AssignSupportInquiryDto |
| Success DTO | SupportInquiryDetailResponseDto |
Assign:
{
"assignedAdminId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1"
}Unassign:
{
"assignedAdminId": null
}7.5 Admin update inquiry status
| Aspect | Details |
|---|---|
| Endpoint | PATCH /api/admin/support-inquiries/:id/status |
| Permission | SupportInquiries_UPDATE |
| Body DTO | UpdateSupportInquiryStatusDto |
| Success DTO | SupportInquiryDetailResponseDto |
Request body:
{
"status": "resolved"
}Allowed statuses:
openactivewaitinglinkedresolvedclosedspam
7.6 Admin link/create support request from inquiry
| Aspect | Details |
|---|---|
| Endpoint | POST /api/admin/support-inquiries/:id/link-support-request |
| Permission | SupportInquiries_UPDATE |
| Body DTO | LinkSupportInquiryRequestDto |
| Success DTO | SupportInquiryDetailResponseDto |
Create new support request:
{
"subject": "Question about a thangka painting"
}Link existing support request:
{
"supportRequestId": 24
}Rules:
- inquiry must have
customerId - existing support request must belong to same customer
- if new request is created, non-system inquiry messages are copied into ticket history
- inquiry status becomes
linked
8. Support Request Endpoint Details
8.1 Mobile create support request
| Aspect | Details |
|---|---|
| Endpoint | POST /api/mobile/support-requests |
| Auth | Customer JWT |
| Body DTO | CreateSupportRequestDto |
| Success DTO | SupportRequestDetailResponseDto |
Request body:
{
"category": "technical",
"subject": "App crashes on checkout",
"message": "I cannot complete checkout on Android."
}Categories:
accountpaymenttechnicalother
8.2 Mobile list support requests
| Aspect | Details |
|---|---|
| Endpoint | GET /api/mobile/support-requests |
| Auth | Customer JWT |
| Query DTO | QuerySupportRequestsDto |
| Success DTO | SupportRequestListItemResponseDto[] paginated |
Only the current customer's requests are returned.
8.3 Mobile get support request detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/mobile/support-requests/:id |
| Auth | Customer JWT |
| Success DTO | SupportRequestDetailResponseDto |
Mobile detail includes:
- category
- subject
- status
- assignment boolean (
isAssigned) - resolution metadata
- chronological messages
8.4 Mobile add support request reply
| Aspect | Details |
|---|---|
| Endpoint | POST /api/mobile/support-requests/:id/messages |
| Auth | Customer JWT |
| Body DTO | CreateSupportRequestMessageDto |
| Success DTO | SupportRequestDetailResponseDto |
Request body:
{
"body": "Here is more detail about the issue."
}Rules:
- closed requests reject replies
- resolved requests reopen to
open - write emits
support.request_message.created
8.5 Admin support request list
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/support-requests |
| Permission | SupportRequests_READ |
| Query DTO | QuerySupportRequestsAdminDto |
| Success DTO | SupportRequestListItemResponseDto[] paginated |
Query params:
| Param | Type | Notes |
|---|---|---|
status | open, in_progress, resolved, closed | optional |
category | account, payment, technical, other | optional |
customerId | uuid | optional |
assignedAdminId | uuid | optional |
search | string | subject match |
page, size, pagination, sort, order | inherited | standard pagination |
8.6 Admin support request detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/support-requests/:id |
| Permission | SupportRequests_READ |
| Success DTO | SupportRequestDetailResponseDto |
Admin detail includes internal author IDs and full message history.
8.7 Admin update support request status
| Aspect | Details |
|---|---|
| Endpoint | PATCH /api/admin/support-requests/:id/status |
| Permission | SupportRequests_UPDATE |
| Body DTO | UpdateSupportRequestStatusDto |
| Success DTO | SupportRequestDetailResponseDto |
Request body:
{
"status": "resolved",
"resolutionNote": "Customer confirmed the issue is fixed."
}Rules:
resolutionNoteis allowed only forresolvedorclosed- status updates record admin activity
8.8 Admin assign support request
| Aspect | Details |
|---|---|
| Endpoint | PATCH /api/admin/support-requests/:id/assign |
| Permission | SupportRequests_UPDATE |
| Body DTO | AssignSupportRequestDto |
| Success DTO | SupportRequestDetailResponseDto |
Request body:
{
"assignedAdminId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1"
}Use null to clear assignment.
8.9 Admin add support request reply
| Aspect | Details |
|---|---|
| Endpoint | POST /api/admin/support-requests/:id/messages |
| Permission | SupportRequests_UPDATE |
| Body DTO | CreateAdminSupportRequestMessageDto |
| Success DTO | SupportRequestDetailResponseDto |
Request body:
{
"body": "Thanks, we are looking into it."
}Side effects:
- writes admin message
- emits
support.request_message.created - notifies customer
- records admin activity
9. Feedback Endpoint Details
9.1 Mobile submit feedback
| Aspect | Details |
|---|---|
| Endpoint | POST /api/mobile/feedback |
| Auth | Customer JWT |
| Body DTO | CreateFeedbackSubmissionDto |
| Success DTO | FeedbackSubmissionResponseDto |
Request body:
{
"category": "bug_report",
"subject": "Search is broken",
"message": "The search screen shows a blank result set."
}Categories:
bug_reportfeature_requestgeneral_feedback
9.2 Admin list feedback
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/feedback |
| Permission | Feedback_READ |
| Query DTO | QueryFeedbackSubmissionsAdminDto |
| Success DTO | FeedbackSubmissionListItemResponseDto[] paginated |
Query params:
| Param | Type | Notes |
|---|---|---|
status | new or reviewed | optional |
category | feedback category | optional |
customerId | uuid | optional |
search | string | subject/message/customer search depending service implementation |
sort | createdAt or updatedAt | default updatedAt |
9.3 Admin feedback detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/feedback/:id |
| Permission | Feedback_READ |
| Success DTO | FeedbackSubmissionResponseDto |
9.4 Admin review feedback
| Aspect | Details |
|---|---|
| Endpoint | PATCH /api/admin/feedback/:id/review |
| Permission | Feedback_UPDATE |
| Body | none |
| Success DTO | FeedbackSubmissionResponseDto |
Side effects:
- sets status to
reviewed - sets
reviewedByAdminId - sets
reviewedAt - records activity
10. DTO Field Reference
10.1 CreateSupportInquiryDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
category | enum | No | inquiry category | defaults to other |
subject | string | Yes | 1-255 chars, trimmed | inquiry subject |
message | string | No | non-blank when present, trimmed | initial message |
guestName | string | No | max 255, trimmed | guest display/contact name |
guestEmail | No | max 320, lowercased | contact email, not identity proof | |
guestPhone | string | No | max 32, trimmed | contact phone |
10.2 IdentifySupportInquiryDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
guestName | string | No | max 255 | update guest contact name |
guestEmail | No | max 320 | update guest email | |
guestPhone | string | No | max 32 | update guest phone |
10.3 CreateSupportInquiryMessageDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
body | string | Yes | non-blank after trim | inquiry message body |
10.4 ConfirmSupportInquiryEmailDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
otp | string | Yes | 4-12 chars | verification code |
10.5 LinkSupportInquiryRequestDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
supportRequestId | integer | No | min 1 | link existing ticket |
subject | string | No | max 255 | subject for new ticket creation |
10.6 Support request DTOs
| DTO | Fields |
|---|---|
CreateSupportRequestDto | category, subject, message |
CreateSupportRequestMessageDto | body |
UpdateSupportRequestStatusDto | status, resolutionNote? |
AssignSupportRequestDto | `assignedAdminId?: string |
10.7 Feedback DTOs
| DTO | Fields |
|---|---|
CreateFeedbackSubmissionDto | category, subject, message |
QueryFeedbackSubmissionsAdminDto | status?, category?, customerId?, search?, pagination/sort |
11. Response Shape Examples
11.1 Support inquiry list item
{
"id": 10,
"trackingCode": "INQ-8K4P2X",
"customerId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"guestEmail": "alex@example.com",
"category": "product",
"subject": "Question about a thangka painting",
"status": "active",
"assignedAdminId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"supportRequestId": null,
"lastVisitorMessageAt": "2026-06-04T10:05:00.000Z",
"lastAdminMessageAt": "2026-06-04T10:06:00.000Z",
"createdAt": "2026-06-04T10:00:00.000Z",
"updatedAt": "2026-06-04T10:06:00.000Z"
}11.2 Support request detail
{
"id": 1,
"customerId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"category": "technical",
"subject": "App crashes on checkout",
"status": "in_progress",
"assignedAdminId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"resolutionNote": null,
"resolvedAt": null,
"closedAt": null,
"lastCustomerMessageAt": "2026-03-17T10:05:00.000Z",
"lastAdminMessageAt": "2026-03-17T10:06:00.000Z",
"messages": [
{
"id": 10,
"supportRequestId": 1,
"authorType": "customer",
"authorCustomerId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"authorAdminId": null,
"authorName": "Alex Customer",
"authorImage": "https://cdn.example.com/customer.png",
"body": "App crashes on checkout.",
"createdAt": "2026-03-17T10:04:00.000Z"
}
],
"createdAt": "2026-03-17T10:00:00.000Z",
"updatedAt": "2026-03-17T10:06:00.000Z"
}11.3 Feedback response
{
"id": 1,
"customerId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"category": "bug_report",
"subject": "Search is broken",
"message": "The search screen shows a blank result set.",
"status": "new",
"reviewedByAdminId": null,
"reviewedAt": null,
"createdAt": "2026-06-04T10:00:00.000Z",
"updatedAt": "2026-06-04T10:00:00.000Z"
}12. Enums
12.1 Support inquiry categories
accountpaymenttechnicalproductorderother
12.2 Support inquiry statuses
openactivewaitinglinkedresolvedclosedspam
12.3 Support inquiry message author types
guestcustomeradminsystem
12.4 Support request categories
accountpaymenttechnicalother
12.5 Support request statuses
openin_progressresolvedclosed
12.6 Support request message author types
customeradmin
12.7 Feedback categories
bug_reportfeature_requestgeneral_feedback
12.8 Feedback statuses
newreviewed
13. Websocket Integration
13.1 Connection auth
Customer/admin JWT:
Authorization: Bearer <jwt>Guest inquiry token:
Authorization: Bearer si_9f5e1c25b0f4475db9f17be7a8c3a123The realtime auth guard recognizes si_... tokens and creates a guest support inquiry actor for inquiry-room authorization.
13.2 Client events
| Event | Payload | Notes |
|---|---|---|
support:join_request_messages | { "supportRequestId": 1 } | join ticket room |
support:leave_request_messages | { "supportRequestId": 1 } | leave ticket room |
support:sync_request_messages | { "supportRequestId": 1, "sinceEventId": "...", "limit": 50 } | replay ticket events |
support:join_inquiry_messages | { "supportInquiryId": 10 } | join inquiry room |
support:leave_inquiry_messages | { "supportInquiryId": 10 } | leave inquiry room |
support:sync_inquiry_messages | { "supportInquiryId": 10, "sinceEventId": "...", "limit": 50 } | replay inquiry events |
13.3 Join response examples
Support request room:
{
"ok": true,
"data": {
"supportRequestId": 1,
"room": "support:request:1:messages",
"roomSize": 2
}
}Support inquiry room:
{
"ok": true,
"data": {
"supportInquiryId": 10,
"trackingCode": "INQ-8K4P2X",
"room": "support:inquiry:10:messages",
"roomSize": 2
}
}13.4 Server event envelope
{
"eventId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"eventType": "support.inquiry_message.created",
"occurredAt": "2026-06-04T10:06:00.000Z",
"data": {
"supportInquiryId": 10,
"trackingCode": "INQ-8K4P2X",
"messageId": 102,
"authorType": "admin",
"authorCustomerId": null,
"authorAdminId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"body": "Thanks, we are checking availability.",
"createdAt": "2026-06-04T10:06:00.000Z",
"actor": {
"type": "ADMIN",
"id": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1"
}
}
}13.5 Sync response
{
"ok": true,
"data": {
"room": "support:inquiry:10:messages",
"events": [],
"replayedCount": 0,
"gapDetected": false
}
}Reconnect recipe:
- Reconnect socket.
- Rejoin every visible support room.
- Call sync event with last processed
eventId. - Deduplicate events by
eventId. - If
gapDetectedis true, refetch REST detail/messages.
14. Integration Diagrams
14.1 Guest inquiry from homepage chat
14.2 Inquiry verification and conversion
14.3 Support request ticket flow
15. Endpoint Reference + Payload Cheatsheet
15.1 Every REST endpoint
| Method | Path | Auth / Permission | Request DTO / Params | Success DTO | Notes |
|---|---|---|---|---|---|
| POST | /api/support-inquiries | optional JWT or guest | CreateSupportInquiryDto | CreateSupportInquiryResponseDto | creates inquiry, greeting, optional first message, guest token |
| GET | /api/support-inquiries | customer JWT | QuerySupportInquiriesDto | SupportInquiryListItemResponseDto[] paginated | current customer's linked inquiries only |
| GET | /api/support-inquiries/:id | customer JWT or si_... | path id int | SupportInquiryDetailResponseDto | validates ownership/token |
| GET | /api/support-inquiries/:id/messages | customer JWT or si_... | path id int | SupportInquiryMessageResponseDto[] | chronological messages |
| POST | /api/support-inquiries/:id/messages | customer JWT or si_... | body body | SupportInquiryDetailResponseDto | emits inquiry websocket event |
| PATCH | /api/support-inquiries/:id/identify | customer JWT or si_... | guest name/email/phone | SupportInquiryDetailResponseDto | stores contact, does not prove identity |
| POST | /api/support-inquiries/:id/verify-email/request | customer JWT or si_... | none | generic response | sends OTP without account enumeration |
| POST | /api/support-inquiries/:id/verify-email/confirm | customer JWT or si_... | otp | SupportInquiryDetailResponseDto | verifies email and links customer if matched |
| GET | /api/admin/support-inquiries | SupportInquiries_READ | admin query | SupportInquiryListItemResponseDto[] paginated | admin triage/search |
| GET | /api/admin/support-inquiries/:id | SupportInquiries_READ | path id int | SupportInquiryDetailResponseDto | admin detail |
| POST | /api/admin/support-inquiries/:id/messages | SupportInquiries_UPDATE | body | SupportInquiryDetailResponseDto | admin reply + realtime + activity |
| PATCH | /api/admin/support-inquiries/:id/assign | SupportInquiries_UPDATE | assignedAdminId | SupportInquiryDetailResponseDto | assign/unassign inquiry |
| PATCH | /api/admin/support-inquiries/:id/status | SupportInquiries_UPDATE | status | SupportInquiryDetailResponseDto | update inquiry lifecycle |
| POST | /api/admin/support-inquiries/:id/link-support-request | SupportInquiries_UPDATE | supportRequestId?, subject? | SupportInquiryDetailResponseDto | link/create ticket after verified customer |
| POST | /api/mobile/support-requests | customer JWT | category, subject, message | SupportRequestDetailResponseDto | create authenticated ticket |
| GET | /api/mobile/support-requests | customer JWT | query filters | SupportRequestListItemResponseDto[] paginated | current customer only |
| GET | /api/mobile/support-requests/:id | customer JWT | path id int | SupportRequestDetailResponseDto | validates ownership |
| POST | /api/mobile/support-requests/:id/messages | customer JWT | body | SupportRequestDetailResponseDto | customer reply + realtime |
| GET | /api/admin/support-requests | SupportRequests_READ | admin query | SupportRequestListItemResponseDto[] paginated | admin ticket list |
| GET | /api/admin/support-requests/:id | SupportRequests_READ | path id int | SupportRequestDetailResponseDto | admin ticket detail |
| PATCH | /api/admin/support-requests/:id/status | SupportRequests_UPDATE | status, resolutionNote? | SupportRequestDetailResponseDto | status update + activity |
| PATCH | /api/admin/support-requests/:id/assign | SupportRequests_UPDATE | assignedAdminId | SupportRequestDetailResponseDto | assign/unassign |
| POST | /api/admin/support-requests/:id/messages | SupportRequests_UPDATE | body | SupportRequestDetailResponseDto | admin reply + notification + realtime |
| POST | /api/mobile/feedback | customer JWT | category, subject, message | FeedbackSubmissionResponseDto | one-way feedback |
| GET | /api/admin/feedback | Feedback_READ | admin query | FeedbackSubmissionListItemResponseDto[] paginated | review queue |
| GET | /api/admin/feedback/:id | Feedback_READ | path id int | FeedbackSubmissionResponseDto | feedback detail |
| PATCH | /api/admin/feedback/:id/review | Feedback_UPDATE | none | FeedbackSubmissionResponseDto | mark reviewed |
15.2 Websocket event cheatsheet
| Direction | Event | Payload | Response/Event |
|---|---|---|---|
| client -> server | support:join_request_messages | { supportRequestId } | join ack with room |
| client -> server | support:leave_request_messages | { supportRequestId } | leave ack |
| client -> server | support:sync_request_messages | { supportRequestId, sinceEventId?, limit? } | replay events |
| server -> client | support.request_message.created | event envelope | message delta |
| client -> server | support:join_inquiry_messages | { supportInquiryId } | join ack with room + trackingCode |
| client -> server | support:leave_inquiry_messages | { supportInquiryId } | leave ack |
| client -> server | support:sync_inquiry_messages | { supportInquiryId, sinceEventId?, limit? } | replay events |
| server -> client | support.inquiry_message.created | event envelope | message delta |
16. Query Variation Examples
16.1 Customer inquiries
| Request | Notes |
|---|---|
GET /api/support-inquiries | default page 1, size 20 |
GET /api/support-inquiries?status=active | active inquiries |
GET /api/support-inquiries?category=product | product inquiries |
GET /api/support-inquiries?pagination=false | all current customer's inquiries |
16.2 Admin inquiries
| Request | Notes |
|---|---|
GET /api/admin/support-inquiries?search=INQ-8K4P2X | tracking code lookup |
GET /api/admin/support-inquiries?search=alex@example.com | guest email lookup |
GET /api/admin/support-inquiries?assignedAdminId=019d... | assigned queue |
GET /api/admin/support-inquiries?status=waiting&sort=updatedAt&order=asc | oldest waiting first |
16.3 Admin support requests
| Request | Notes |
|---|---|
GET /api/admin/support-requests?status=open | open tickets |
GET /api/admin/support-requests?category=technical | technical tickets |
GET /api/admin/support-requests?customerId=019d... | customer-specific tickets |
GET /api/admin/support-requests?search=checkout | subject search |
16.4 Admin feedback
| Request | Notes |
|---|---|
GET /api/admin/feedback?status=new | review queue |
GET /api/admin/feedback?category=bug_report | bug reports |
GET /api/admin/feedback?customerId=019d... | customer feedback |
17. Error Handling
All support API errors use the standard error envelope from the global exception filter.
Example:
{
"statusCode": 403,
"errorCode": "SUPPORT_INQUIRY_TOKEN_INVALID",
"message": "Support inquiry access token is invalid or expired.",
"timestamp": "2026-06-04T10:00:00.000Z",
"path": "/api/support-inquiries/10"
}Representative error map:
| HTTP | errorCode | Scenario |
|---|---|---|
| 400 | SUPPORT_INQUIRY_CLOSED | message attempted on closed/spam inquiry |
| 400 | SUPPORT_INQUIRY_LINK_INVALID | invalid support request link target |
| 400 | SUPPORT_INQUIRY_EMAIL_VERIFICATION_INVALID | invalid OTP or missing email |
| 400 | SUPPORT_INQUIRY_SUPPORT_REQUEST_REQUIRED | conversion requires verified customer |
| 403 | SUPPORT_INQUIRY_ACCESS_DENIED | JWT/token cannot access inquiry |
| 403 | SUPPORT_INQUIRY_TOKEN_INVALID | missing/invalid/expired guest token |
| 404 | SUPPORT_INQUIRY_NOT_FOUND | inquiry not found |
| 429 | RATE_LIMIT_EXCEEDED | public inquiry write limit exceeded |
Frontend guidance:
- Use
errorCodefor UI branching. - Treat inquiry token errors as expired guest sessions.
- Treat email verification failures as retry/request-new-code flows.
- Treat closed inquiry errors as read-only conversation state.
18. Testing Scenarios
18.1 Guest starts support inquiry
- Call
POST /api/support-inquirieswithout JWT. - Expect
trackingCode, messages, andinquiryAccessToken. - Store token client-side for the chat session.
- Fetch
GET /api/support-inquiries/:idwithBearer si_.... - Expect the same inquiry and full message history.
18.2 Guest sends and receives live messages
- Create guest inquiry.
- Connect websocket with
Bearer si_.... - Emit
support:join_inquiry_messages. - Post
POST /api/support-inquiries/:id/messages. - Expect REST detail response and websocket
support.inquiry_message.created.
18.3 Guest email verification
- Identify inquiry with
guestEmail. - Request verification.
- Confirm OTP.
- If email belongs to customer, expect
customerIdon inquiry. - If email does not belong to customer, expect
emailVerifiedAtbut no customer link. - In both cases, frontend must not learn whether the email matched before confirmation.
18.4 Admin converts inquiry to support request
- Admin opens inquiry detail.
- Verify
customerIdexists. - Call
POST /api/admin/support-inquiries/:id/link-support-request. - Expect
supportRequestIdand statuslinked. - Open
/api/admin/support-requests/:supportRequestId. - Verify copied message history.
18.5 Existing mobile support request
- Customer calls
POST /api/mobile/support-requests. - Customer sees request in
GET /api/mobile/support-requests. - Admin sees request in
GET /api/admin/support-requests. - Admin replies.
- Customer receives websocket event and can refetch detail.
18.6 Feedback review
- Customer calls
POST /api/mobile/feedback. - Admin lists
GET /api/admin/feedback?status=new. - Admin opens detail.
- Admin calls
PATCH /api/admin/feedback/:id/review. - Expect
status=reviewed,reviewedAt, andreviewedByAdminId.
19. Integration Recipes
19.1 Website homepage chat widget
- Create inquiry when visitor opens chat or sends first message.
- Store
id,trackingCode, andinquiryAccessToken. - Show tracking code in the widget.
- Use REST to send messages.
- Use websocket to receive admin replies.
- Prompt for email only as contact/follow-up information.
- Use verification endpoints before assuming customer identity.
- When token expires, ask visitor to start a new inquiry or log in.
19.2 Admin support inbox
- Load open inquiries with
GET /api/admin/support-inquiries?status=open. - Search by tracking code, email, phone, or subject.
- Assign inquiry to self.
- Join websocket inquiry room for live updates.
- Reply through REST.
- Resolve, close, mark spam, or link to support request.
19.3 Mobile support center
- Use
/api/mobile/support-requestsfor logged-in customer ticket history. - Use
/api/mobile/support-requests/:id/messagesfor ticket replies. - Join
support:join_request_messagesfor open ticket detail screens. - On reconnect, call
support:sync_request_messages. - If replay gap is detected, refetch ticket detail.
19.4 Admin feedback queue
- Show
GET /api/admin/feedback?status=new. - Filter by category.
- Let admin open detail.
- Review action calls
PATCH /api/admin/feedback/:id/review. - Remove reviewed item from new queue.
20. Operational Notes
- Run DB migrations before deploying support inquiry code.
- Configure role permissions for
SupportInquiries. - Keep
EVENT_HISTORY_ENABLED=truefor reconnect replay. - Keep
SUPPORT_INQUIRIES_INACTIVE_CLOSE_DAYSaligned with product support policy. - Guest
si_...token is sensitive and should not be logged. - Tracking code is safe to show to customers and admins.
- Full chat history remains available after close; close is not delete.
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 lifecycle and product behavior.
- Backend Reference: See Support - Backend Documentation for schema and service architecture.