Skip to content

PRD: Sales Inquiries

ModuleCustomer (CORE-09)PRD IDPRD-INQ-001
StatusShippedOwnerCustomer squad
Date2026-04-06Versionv1.0
Packages@nx/outreach · @nx/coreURDINQ

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

MetricTarget / signal
Notification latencyNew inquiries surface to admins in real time (sub-second WebSocket push), not by polling
Capture completeness100% of public submits persist with contact + business info + message
Lead accountabilityEvery inquiry shows an assignee, reply author/timestamp, and a terminal outcome
Time to first responseMedian NEW → replied time per merchant trends down

4. Personas & Use Cases

PersonaGoal in this feature
Site Visitor (Lead)Submit an inquiry from the public site without an account
SalesSee new leads instantly, claim them, reply, and resolve as converted or lost
Owner / AdminWatch 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

#RequirementURD ref
FR-1Capture an inquiry from the public submit endpoint with contact info, business info, and message; new inquiry starts in status NEWURD-INQ-001
FR-2On submission, emit a real-time WebSocket notification to admins carrying the inquiry id, type, name, email, business name, subject, and created-atURD-INQ-002
FR-3Notification 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 targetedURD-INQ-002
FR-4Track an inquiry's assignment, reply (author + timestamp), conversion, and lost reasonURD-INQ-003
FR-5Move an inquiry through the lifecycle NEW → assigned → replied → converted/lostURD-INQ-004
FR-6Manage inquiries via CRUD (find / find-by-id / count / create / update / delete), each gated by an InquiryPermissions.* resourceURD-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

AreaRequirement
Real-time deliveryWebSocket emitter is Redis-backed and runs in single or cluster mode from env; notification fans out across instances
Graceful degradationIf 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 & authzInquiry management endpoints are merchant-scoped (x-merchant-id) and gated by InquiryPermissions; the public submit is unauthenticated
Data integrityAll inquiry records use soft-delete, preserving lead history
i18nUser-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

EntityRole
InquiryThe lead record - contact info, business info, message, type, status, assignee, reply, and outcome (converted/lost reason)
WebSocket topic / roomINQUIRY_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 inquiry schema.ts / model.ts and 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/client consumes 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 / questionMitigation / status
Socket service down → admins miss the notificationSubmit 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 instancesRedis-backed emitter in cluster mode handles multi-instance delivery
Lead spam / abuse on a public endpointOpen: consider rate-limiting / captcha on the public submit
No email follow-up channelOut of scope; campaign engine is Planned in the URL

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 - see URD feature catalog (INQ)
RolloutAll merchants; no feature flag
MigrationNone (inquiry schema/model fields added in @nx/core)
Launch criteriaPublic submit persists a NEW inquiry; admins receive the real-time notification end-to-end; assign → reply → converted/lost lifecycle verified
MonitoringInquiry 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

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.