Skip to Content

Self-Signup: BRD vs. Implementation Gap Analysis

Date: April 10, 2026 Source BRD: docs/self-signup/self signup.md Status: Updated April 13, 2026 — Phase 12 + UX polish + E2E test complete


Summary of Resolution (Phase 12 — /signup wizard)

This document was originally written against the legacy /register wizard. A new wizard at /signup was built (Phase 12) that addresses all critical BRD gaps. The analysis below still describes the OLD /register flow for reference.

Phase 12 resolved items (confirmed via full browser E2E test, April 10, 2026):

GapResolution
Payment not collected at sign-up✅ Razorpay Checkout modal at Step 4; first-month invoice created on submit
Step 2 field mismatch✅ New Step 2: Company Name + Industry (required) + Website (optional) — matches BRD
PAN in wrong step✅ PAN moved to Step 3 (location), correct per BRD
SEZ field missing✅ SEZ status field added to Step 3 (auto-affects tax rate)
Billing email missing✅ Billing Email field added to Step 4 (auto-filled from Step 1)
Session TTL 1hr → 1 month✅ 30-day rolling TTL; /signup/expired page on expiry
Expired session page/signup/expired with “Start New Signup” + “Contact Sales” links
currentStage enumRegistrationSession.currentStage = step_1/step_2/step_3/step_4/complete
Abandonment emails 1–3✅ Scheduler worker (apps/servers/scheduler) with signup-reminder-early, signup-reminder-late, signup-alert-internal handlers
createdVia: "self-signup"✅ Set on Tenant record at registration complete
signupSessionId on Tenant✅ Back-referenced at registration complete

April 13, 2026 — UX polish + API fixes + E2E test:

ItemResolution
Country / Industry / State dropdowns were native <select>✅ All replaced with custom searchable comboboxes on S1, S2, S3
Billing State + Country on S4 were read-only✅ Now editable searchable comboboxes (pre-filled from prior steps)
Busy indicator missing on Continue buttons✅ Spinner + disabled state added to all 4 steps
billingAddress not saved to Tenant.address✅ Fixed in register/complete — Business Info page now shows address
companyWebsite silently dropped at registration✅ Fixed — now saved to Tenant.website
No full E2E test for the signup flowapps/dashboard/tests/e2e/signup-flow.dev.spec.ts — full 4-step flow, DB reset, login verification

April 13, 2026 — Password reset email (follow-up fix):

ItemResolution
Forgot password email sending blank body✅ Added "reset_password" and "password-reset" email templates to packages/db/src/seed.ts
type: "reset_password" wrong in callback✅ Fixed at the time (now superseded — see below)
currentYear missing from reset email variables✅ Added to both sendResetPassword and POST /auth/v1/forgot-password callers

Note (historical): At the time this was written, forgot-password used Better Auth (authClient.requestPasswordReset()). Better Auth was removed in April 2026. All portals now use POST /auth/v1/forgot-passwordPOST /auth/v1/reset-password on the Fastify API. The reset link goes to /reset-password?token=... on the dashboard (port 3000). The slug is "password-reset" (not "reset_password"). See Auth Overview.

Remaining items (medium/low priority):

  • title (Mr./Mrs.) still not persisted to DB or API
  • Payment-pending dashboard banner (if Razorpay payment fails after account creation)
  • Full stepHistory timestamps
  • UTM source/medium capture

Legacy /register Wizard Gap Analysis (historical reference)


Step-by-Step Field Gaps

Step 1 — Contact / Account Setup

BRD RequirementStatus
Title (Mr./Mrs./Ms.)✅ UI exists — ❌ never sent to API or stored in DB
Country, First/Last Name, Email, Mobile, Password✅ Implemented
Password “Generate” button✅ Implemented
Validation on Continue only (not on blur)Needs verification
”This email is already registered” uniqueness errorNeeds verification

Step 2 — Company / Business Details

Fields are completely different from the BRD:

BRD FieldCode FieldStatus
Company Name (single field)Legal Name + Brand Name (two fields)❌ BRD specifies one field
Company Website (optional, valid URL)(not present)❌ Missing
Industry (predefined dropdown)(not present)❌ Missing
(PAN is in Step 3 per BRD)PAN Number placed here instead❌ Wrong step

Step 3 — Tax & Region (India Only)

BRD RequirementStatus
State selection✅ Implemented
”Do you have GST?” Yes/No radio✅ Implemented
GST Number (conditional on GST = Yes)✅ Implemented
SEZ field (conditional on GST = Yes)❌ Missing entirely
PAN Number (optional, belongs in Step 3 per BRD)❌ Placed in Step 2 instead
Auto-skip / auto-complete Step 3 for non-India users❌ Step is non-fatal but no auto-complete logic
Tax rate calculation displayed on Step 4❌ Not implemented

Step 4 — Plan Selection & Billing Address

BRD RequirementStatus
Plan cards with pricing (India ₹ / UAE AED)Depends on seeded plans — UI exists
Tax breakdown shown on plan card❌ Missing
Billing Address (13 fields per BRD)Partial — only 4 fields sent to API
billingPostal❌ Collected in UI but never sent to API
billingState❌ Collected in UI but never sent to API
billingEmail (auto-fill from Step 1)❌ Field missing entirely
Terms of Service + Privacy Policy checkboxesNeeds verification
GST / tax data persisted at account creation❌ Silently dropped — POST /auth/v1/register/complete does not accept it

Session Management Gaps

BRD RequirementCode Reality
Session valid 1 month❌ 1-hour rolling TTL — major discrepancy
Session link emailed on Step 1 completion (/signup?session={id})❌ Not implemented
On return: password re-entry modal❌ No modal — silent redirect to /register
Expired session page with “Start New Signup” / “Contact Sales” options❌ No dedicated page — silent redirect only
currentStage enum field (7 states: step_1complete)❌ Only step integer (1–4) exists in DB
stepHistory array with per-step timestamps❌ Not in DB
UTM source + UTM medium captured from URL❌ Not captured anywhere (frontend, API, or DB)
createdVia: "self-signup" on tenant record❌ Not set
signupSessionId back-reference on tenant record❌ Not stored

Payment Flow Gaps

Per the BRD, payment must be initiated at Step 4 submission before the user is redirected. Currently POST /auth/v1/register/complete creates the account and returns tokens, but no payment is triggered.

BRD RequirementStatus
Invoice created (Unpaid) on Step 4 submit❌ No invoice created at registration
Razorpay order created on Step 4 submit❌ Not triggered from registration flow
Razorpay modal launched immediately after account creation❌ Missing — redirects straight to /login?registered=1
Payment success → subscription activated + welcome email❌ Subscription created as active regardless of payment
Payment failure: “Retry Payment” option❌ Missing
Payment failure: “Pay via Bank Transfer” (show bank details)❌ Missing
Payment failure: “Contact Sales” option❌ Missing
User can log in with payment pending + dashboard banner❌ No payment-pending banner in dashboard
Pay outstanding invoice from dashboard UI❌ API routes exist (/tenant/v1/billing/*) but no dashboard UI

Email & Abandonment Gaps

All three abandonment email flows and the internal sales alert are completely unimplemented. Only the welcome email is queued (via BullMQ), though its timing relative to payment outcome does not match the BRD.

EmailTrigger (BRD)Status
Email 1 — Completion Reminder (steps 1–2 drop-off)1hr + 24hr after abandonment❌ Not implemented
Email 2 — “Almost There” Reminder (step 3 complete, step 4 not)1hr, 24hr, 3 days, 5 days❌ Not implemented
Email 3 — Internal Sales Alert1hr after any abandonment (once per session)❌ Not implemented
Email 4 — Welcome EmailImmediately after Step 4 submit, regardless of payment⚠️ Enqueued via BullMQ on account creation — but not conditioned on payment outcome

Database / Schema Gaps

Field / RequirementStatus
currentStage enum on RegistrationSession❌ Missing — only step int exists
stepHistory array with timestamps❌ Missing
UTM source + UTM medium fields on session❌ Missing
Email tracking flags (reminder-sent booleans) on session❌ Missing
SEZ status field on session❌ Missing
Industry field on session / tenant❌ Missing entirely
Company website field on session / tenant❌ Missing entirely
Full billing address (all 13 fields) persisted to DB❌ Only 3 of 13 fields (billingAddress, billingCity, billingCountry) reach the DB
createdVia field on Tenant model❌ Missing
signupSessionId field on Tenant model❌ Missing
title (Mr./Mrs./Ms.) on session / user❌ Collected in UI, not persisted anywhere

Other Code Issues Found

IssueFileNotes
title silently droppedapps/dashboard/src/app/(auth)/register/page.tsxStored in sessionStorage only; not included in POST /auth/v1/register/start body
billingPostal + billingState silently droppedapps/dashboard/src/app/(auth)/register/plan/page.tsxSaved to sessionStorage but excluded from POST /auth/v1/register/complete body
timezone accepted by API but never sentapps/api/src/routers/auth.tsSchema accepts it; frontend never includes it
Orphaned legacy routeapps/dashboard/src/app/api/register/route.tsDead code — incompatible 4-field contract, superseded by the multi-step flow
subscription.charged webhook stubbedapps/api/src/routers/pgcallbacks.tsAutomated billing-cycle renewal does nothing
subscription.cancelled webhook stubbedapps/api/src/routers/pgcallbacks.tsLogs CRITICAL but does not update any DB record

Prioritised Gap List

Critical — blocks core BRD flow

  1. Payment gateway integration at registration (Razorpay order + invoice on Step 4 submit)
  2. Billing address + GST/tax fields not reaching the DB from register/complete
  3. Step 2 field mismatch (missing Industry + Website; PAN in wrong step)
  4. Session TTL: 1 hour → 1 month

High — significant missing features

  1. All 3 abandonment email flows + internal sales alert (Emails 1–3)
  2. SEZ field in Step 3 + tax rate calculation displayed on Step 4
  3. Session link email sent on Step 1 completion
  4. Session restoration with password re-entry modal
  5. Expired session dedicated page (/signup?session=... expired flow)
  6. currentStage / stepHistory / UTM / email-flag fields in DB schema
  7. createdVia: "self-signup" + signupSessionId on Tenant record

Medium

  1. title not persisted to DB or API
  2. billingEmail field missing from Step 4
  3. Payment-pending banner in tenant dashboard
  4. Pay invoice from dashboard UI (API exists, no UI)

Low / BRD Future Enhancements (not yet due)

  1. IP / geolocation capture
  2. Single “Full Name” field instead of Title + First + Last
  3. Password field moved to Step 4
  4. Post-signup profile editing

Deferred Ideas — Under Consideration

Session token in URL for cross-tab / cross-device continuation

Raised: April 2026
Status: Deferred — needs security review before implementing

Idea: Append ?session=TOKEN to the URL on steps 2–4 so users can copy the address bar URL and resume the signup in a different tab or on a different device — without waiting for the resume email.

Why it’s worth considering:

  • The ?session=TOKEN mechanism already exists and is used by the email resume link (/signup?session=...), so the token is already designed to leave the browser
  • Step 1 already reads and restores from ?session= — steps 2–4 would just extend the same pattern
  • sessionStorage is tab-isolated: opening a new tab currently loses all state and shows “Invalid or expired session token”, which is a real UX pain point

Security considerations to resolve before implementing:

  • Session tokens in URLs appear in browser history, server access logs, and Referer headers on outbound link clicks — evaluate whether this is acceptable for a signup-only token
  • Consider whether the token scope (signup session only, not full account access) makes the risk low enough
  • Evaluate whether a short-lived one-time copy link (generated on demand) is safer than always-in-URL

Suggested implementation when ready:

  1. After S1 completes, use router.replace to push ?session=TOKEN into the URL on S2/S3/S4 without a page reload
  2. Each step reads token from URL param as fallback when sessionStorage is empty (same useSearchParams pattern already in S1)
  3. Optionally add a “Copy link to continue later” button as a deliberate action rather than always-in-URL

© 2026 Leadmetrics — Internal use only