PRD: Sales Inquiries
| Module | Customer (CORE-09) | PRD ID | PRD-INQ-001 |
| Status | Shipped | Owner | Customer squad |
| Date | 2026-04-06 | Version | v1.0 |
| Packages | @nx/outreach · @nx/core | URD | INQ |
TL;DR
Lets a prospect submit a sales inquiry from the public marketing site - contact details, business info, and a message - and gives the back-office sales team a real-time signal the moment a lead lands. Each inquiry is tracked through a clear NEW → assigned → replied → converted/lost lifecycle, so no lead arrives unseen and every follow-up has an audit trail.
1. Context & Problem
The public Overture marketing site is where prospects reach out, but a lead is only valuable if the sales team sees it and acts on it. Without a structured capture-and-notify path, inquiries either sit in an inbox or require the team to poll a list - leads go stale, ownership is unclear, and there is no record of who replied or how the inquiry resolved. As KICKO targets HKD/SME businesses through self-serve sign-up, the inbound lead funnel must be reliable and accountable.
This feature captures the inquiry as a first-class record in @nx/outreach, pushes a real-time WebSocket signal to admins on submission, and tracks each lead through a controlled lifecycle so sales can assign, reply, and resolve it.
2. Goals & Non-Goals
Goals
- Capture an inquiry from the public site with contact info, business info, and a message in a single submit.
- Notify admins in real time over WebSocket the moment a new inquiry is submitted - no polling.
- Track each inquiry's assignment, reply, conversion, and lost reason through a lifecycle (NEW → assigned → replied → converted/lost).
Non-Goals
- Email / SMS campaign engine (Planned - see URD §7).
- Customer segmentation and targeting.
- Inquiry / lead lifetime-value analytics.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Notification latency | New inquiries surface to admins in real time (sub-second WebSocket push), not by polling |
| Capture completeness | 100% of public submits persist with contact + business info + message |
| Lead accountability | Every inquiry shows an assignee, reply author/timestamp, and a terminal outcome |
| Time to first response | Median NEW → replied time per merchant trends down |
4. Personas & Use Cases
| Persona | Goal in this feature |
|---|---|
| Site Visitor (Lead) | Submit an inquiry from the public site without an account |
| Sales | See new leads instantly, claim them, reply, and resolve as converted or lost |
| Owner / Admin | Watch inbound lead volume and ensure no inquiry goes unhandled |
Core scenarios: a visitor submits an inquiry → it is stored as NEW and admins are notified in real time → a sales rep assigns it to self → replies (author + timestamp recorded) → marks it converted or lost with a reason.
5. User Stories
- As a site visitor, I want to submit an inquiry with my contact and business details and a message, so the business can follow up with me.
- As a sales rep, I want to be notified in real time when a new inquiry arrives, so I can respond quickly without polling a list.
- As a sales rep, I want to assign an inquiry to myself, so ownership is clear.
- As a sales rep, I want to reply to an inquiry and have my reply and timestamp recorded, so the follow-up is auditable.
- As a sales rep, I want to mark an inquiry converted or lost (with a reason), so the lead's outcome is captured.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Capture an inquiry from the public submit endpoint with contact info, business info, and message; new inquiry starts in status NEW | URD-INQ-001 |
| FR-2 | On submission, emit a real-time WebSocket notification to admins carrying the inquiry id, type, name, email, business name, subject, and created-at | URD-INQ-002 |
| FR-3 | Notification is published to an INQUIRY_SUBMITTED topic, fanned out to a collection-wide room and a per-inquiryId room so a specific inquiry can be targeted | URD-INQ-002 |
| FR-4 | Track an inquiry's assignment, reply (author + timestamp), conversion, and lost reason | URD-INQ-003 |
| FR-5 | Move an inquiry through the lifecycle NEW → assigned → replied → converted/lost | URD-INQ-004 |
| FR-6 | Manage inquiries via CRUD (find / find-by-id / count / create / update / delete), each gated by an InquiryPermissions.* resource | URD-INQ-003..004 |
Full requirement text and acceptance criteria live in the Customer URD. This PRD references them rather than restating them.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Real-time delivery | WebSocket emitter is Redis-backed and runs in single or cluster mode from env; notification fans out across instances |
| Graceful degradation | If the socket service is not ready, the submit still persists the inquiry and the notification no-ops with a warning - capture never fails on the notify path |
| Tenancy & authz | Inquiry management endpoints are merchant-scoped (x-merchant-id) and gated by InquiryPermissions; the public submit is unauthenticated |
| Data integrity | All inquiry records use soft-delete, preserving lead history |
| i18n | User-facing labels/statuses are bilingual ({ en, vi }) |
8. UX & Flows
Key screens: the public inquiry form lives on the Overture marketing site; the back-office inquiry list, detail, and lifecycle controls (with a real-time arrival badge/toast) live in apps/client.
9. Data & Domain
| Entity | Role |
|---|---|
Inquiry | The lead record - contact info, business info, message, type, status, assignee, reply, and outcome (converted/lost reason) |
| WebSocket topic / room | INQUIRY_SUBMITTED topic (observation/outreach/inquiry/submitted); a collection room plus a per-inquiryId room for targeted delivery |
Conceptual only - full schema and invariants in the Outreach domain model.
10. Dependencies & Assumptions
Depends on
@nx/outreach- owns the inquiry record, CRUD lifecycle, and the WebSocket component.@nx/core- shared inquiryschema.ts/model.tsand the base WebSocket emitter / socket-event primitives.- Redis - backs the WebSocket emitter for cross-instance fan-out.
Assumptions
- The public Overture site renders the inquiry form and posts to the submit endpoint.
- A client-side subscriber (sidebar badge / realtime toast) in
apps/clientconsumes the notification. - Email delivery (SMTP) is out of scope for this module; follow-up happens through the lifecycle, not via this feature.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Socket service down → admins miss the notification | Submit still persists; notify no-ops with a warning, so the inquiry is never lost - admins still see it in the list |
| Notification fan-out across instances | Redis-backed emitter in cluster mode handles multi-instance delivery |
| Lead spam / abuse on a public endpoint | Open: consider rate-limiting / captcha on the public submit |
| No email follow-up channel | Out of scope; campaign engine is Planned in the URL |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P2 - see URD feature catalog (INQ) |
| Rollout | All merchants; no feature flag |
| Migration | None (inquiry schema/model fields added in @nx/core) |
| Launch criteria | Public submit persists a NEW inquiry; admins receive the real-time notification end-to-end; assign → reply → converted/lost lifecycle verified |
| Monitoring | Inquiry volume per merchant, notification send success/failure rate, NEW → replied time |
13. FAQ
Do admins have to refresh to see a new inquiry? No - a WebSocket notification is pushed on submission, so a new lead surfaces in real time.
What happens if the WebSocket service is down when an inquiry is submitted? The inquiry is still persisted; the notification is skipped with a warning. Admins still see it in the inquiry list - capture never depends on the notify path.
Can a visitor submit an inquiry without an account? Yes - the public submit endpoint is unauthenticated. Inquiry management (find/assign/reply/resolve) is authenticated and permission-gated.
Does submitting an inquiry send the lead an email? No - there is no email/SMS channel in this module. Follow-up is tracked through the inquiry lifecycle.
What are the inquiry statuses? NEW → assigned → replied → converted/lost, with a lost reason captured when applicable.
References
- URD: Customer - Sales Inquiries (INQ area)
- Module: Customer - overview + traceability
- Developer: @nx/outreach · Outreach domain model