User Registration
The front door of the platform. Owners, managers and travellers all enter through this flow. The goal: friction low enough that real users do not bounce, signals strong enough that fakes do not get through.
Who registers, and where
ABc has two distinct sign-up surfaces. They share an account model but enter through different doors.
Owner / Manager sign-up
From app.abc.com/signup. Asks for business name on step 1 so we know to route them to the KYC flow next. Default role: owner.
Public traveller sign-up
From abc.com/signup or inline during checkout. Lighter: just name, email, phone, password. Default role: public_user.
Required fields
| Field | Owner | Public | Validation |
|---|---|---|---|
| Full name | Required | Required | 2–80 chars, no digits |
| Required | Required | RFC 5322, unique, DNS-MX check | |
| Phone | Required | Required | E.164 with country code, libphonenumber-validated |
| Password | Required | Required | Min 10 chars, mixed case, 1 digit, not in HIBP |
| Business name | Required | — | 2–120 chars |
| Property type | Required | — | hotel · bnb · villa · hostel · other |
| City | Required | Optional | From dropdown (geo-data seeded) |
| How did you hear about ABc | Optional | — | Free text · for attribution |
Registration flow
-
User opens the sign-up page
Front-end loads the form. A hidden honeypot field is added — bots fill it, humans never see it.
-
User submits the form
Client-side validation runs first (instant feedback). On submit, we hit
POST /auth/signupwith the payload + an hCaptcha token. -
Server pre-checks
Honeypot empty? hCaptcha valid? Email not already used? Phone not already used? Email domain not on disposable-email blocklist?
-
Create user (pending state)
Insert into
userswithstatus = pending_verification, password hashed (Argon2id). No login granted yet. -
Send OTP to phone + magic link to email
Phone gets a 6-digit code (SMS, 10-min expiry). Email gets a one-time link (15-min expiry). Both queued — user must verify both to activate.
-
User verifies
On verification, mark the relevant flag true. When
email_verifiedANDphone_verified→ flipstatus = active→ issue session. -
Route based on role
Owners land on the property onboarding wizard. Public users land on home with a "complete your profile" nudge.
UI — sign-up form (owner)
API contract
// Request { "name": "Rohan Mehta", "email": "rohan@parkviewhotel.in", "phone": "+919812345621", "password": "••••••••••", "role": "owner", "business_name": "Park View Hotel", "property_type": "hotel", "city": "Manali", "hcaptcha_token": "...", "hp": "" // honeypot, must be empty } // Response — 201 Created { "user_id": "usr_01HW…", "status": "pending_verification", "next": { "email_otp_required": true, "phone_otp_required": true, "resend_after_seconds": 30 } } // Error — 409 Conflict (email taken) { "error": "email_in_use", "message": "That email is already registered." }
Anti-bot & anti-abuse
hCaptcha
Invisible challenge; falls back to interactive on risk score > 0.7. Token verified server-side before user is created.
Honeypot field
Hidden field hp. If filled, return 200 OK with a fake token but skip user creation. Bot wastes its retry budget.
Rate limits
5 sign-ups / IP / hour. 3 OTP sends / phone / hour. Exponential backoff on failed OTP entry.
Disposable email block
Maintain a list (e.g. mailinator.com, tempmail.com) — refuse sign-up with a friendly message.
Phone reuse
One phone can have one owner account. Public-side allows up to 3 to keep family bookings simple.
Risk score on save
If risk score > threshold (new domain + new IP + suspicious UA), flag for manual review before activation.
Common error states & copy
| Case | HTTP | What the user sees |
|---|---|---|
| Email already used | 409 | "That email is already registered. Sign in or use forgot password." |
| Phone already used | 409 | "This phone number is already on ABc. Use a different number or sign in." |
| Weak password | 422 | "Use at least 10 characters with mixed case and a number." |
| Disposable email | 422 | "Please use your work or personal email — we need to reach you." |
| Captcha failed | 400 | "Verification failed. Please try again." |
| Rate limited | 429 | "Too many sign-ups from this network. Try again in an hour." |
Database fields
users
- iduuid
- nametext
- emailtext uniq
- phonetext
- password_hashtext
- roleenum
- statusenum
- email_verifiedbool
- phone_verifiedbool
- risk_scorefloat
- created_attimestamp
otp_tokens
- iduuid
- user_id→ users
- channelemail | sms
- code_hashtext
- attemptsint
- expires_attimestamp
- consumed_attimestamp
After this page, owners proceed to Verification & Anti-Fraud (KYC). The OTP step lives in this page; the KYC document upload lives in the next.